---
title: Syntax
description: Reference for the Nextflow language syntax.
---
# Syntax
This page provides a comprehensive description of the Nextflow language.
## Comments
A line comment starts with `//` and includes the rest of the line.
```nextflow
println 'Hello world!' // line comment
```
A block comment starts with `/*` and includes all subsequent characters up to the first `*/`.
```nextflow
/*
* block comment
*/
println 'Hello again!'
```
## Script declarations
A Nextflow script may contain the following top-level declarations:
- Shebang
- Feature flags
- Include declarations
- Params block
- Parameter declarations (legacy)
- Workflow definitions
- Process definitions
- Agent definitions
- Function definitions
- Enum types
- Record types
- Output block
Script declarations are in turn composed of statements and expressions.
If there are no script declarations, a script may contain one or more [statements](#statements), in which case the entire script is treated as an entry workflow. For example:
```nextflow
println 'Hello world!'
```
Is equivalent to:
```nextflow
workflow {
println 'Hello world!'
}
```
:::warning
Statements and script declarations can not be mixed at the same level.
:::
### Shebang
The first line of a script can be a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)):
```sh
#!/usr/bin/env nextflow
```
### Feature flag
A feature flag declaration is an assignment. The target should be a valid [feature flag][config-feature-flags] and the source should be a literal (i.e. number, string, boolean):
```nextflow
nextflow.preview.recursion = true
```
### Include
An include declaration consists of an *include source* and one or more *include clauses*:
```nextflow
include { hello as sayHello } from './some/module'
```
The include source should be a string literal. Each include clause should specify a name, and may also specify an *alias*. In the above example, `hello` is included under the alias `sayHello`.
:::note
[Enum](#enum-type) and [record](#record-type) types cannot be aliased. They must be included under their original name. The `params` block of an included pipeline is the exception -- it is a type created for the include, so it must be aliased.
:::
Include clauses can be separated by semi-colons or newlines:
```nextflow
// semi-colons
include { hello ; bye as goodbye } from './some/module'
// newlines
include {
hello
bye as goodbye
} from './some/module'
```
Include clauses can also be specified as separate includes:
```nextflow
include { hello } from './some/module'
include { bye as goodbye } from './some/module'
```
The `workflow` name refers to the entire pipeline defined by the included script, i.e., its params block, entry workflow, and output block. The `params` name refers to the params block as a record type. Each of these names must be given an alias:
```nextflow
include {
params as RnaseqParams ;
workflow as RNASEQ
} from './pipelines/rnaseq.nf'
```
See [pipeline composition][pipeline-composition] for more information.
### Params block
The params block consists of one or more *parameter declarations*. A parameter declaration consists of a name, type, and an optional default value:
```nextflow
params {
input: Path
max_iterations: Integer = 100
}
```
Only one params block may be defined in a script.
### Parameter (legacy)
A legacy parameter declaration is an assignment. The target should be a pipeline parameter and the source should be an expression:
```nextflow
params.message = 'Hello world!'
```
Parameters supplied via command line options, params files, and config files take precedence over parameter definitions in a script.
### Workflow
A workflow can be a *named workflow* or an *entry workflow*.
A *named workflow* consists of a name and a body, and may consist of a *take*, *main*, and *emit* section:
```nextflow
workflow greet {
take:
greetings
main:
messages = greetings.map { v -> "$v world!" }
emit:
messages
}
```
- The take, emit, and publish sections are optional. The `main:` section label can be omitted if they are not specified.
- The take section consists of one or more parameters.
- The main section consists of one or more [statements](#statements).
- The emit section consists of one or more *emit statements*. An emit statement can be a [variable name](#variable), an [assignment](#assignment), or an [expression statement](#expression-statement). If an emit statement is an expression statement, it must be the only emit.
An *entry workflow* has no name and may consist of a *main*, *publish*, *onComplete*, and *onError* section:
```nextflow
workflow {
main:
greetings = channel.of('Bonjour', 'Ciao', 'Hello', 'Hola')
messages = greetings.map { v -> "$v world!" }
greetings.view { v -> "$v world!" }
publish:
messages = messages
onComplete:
log.info 'Workflow completed successfully!'
onError:
log.error 'Workflow failed.'
}
```
- Only one entry workflow may be defined in a script.
- The `main:` section label can be omitted if the other sections are not specified.
- The publish section consists of one or more *publish statements*. A publish statement is an [assignment](#assignment), where the assignment target is the name of a workflow output.
- The `onComplete` and `onError` sections consist of one or more [statements](#statements).
### Workflow (typed)
A typed workflow is a workflow that uses static typing for inputs and outputs:
```nextflow
nextflow.enable.types = true
workflow greet {
take:
greetings: Channel
main:
messages = greetings.map { v -> "$v world!" }
emit:
messages: Channel
}
```
Typed workflows have the following new features:
- Each workflow input in the `take:` section has a name and a type.
- Each named workflow output in the `emit:` section may specify a type.
See [Typed workflows][workflow-typed-page] for more information on the semantics of typed workflows.
### Process
A process consists of a name and a body. The process body consists of one or more [statements](#statements). A minimal process definition must return a string:
```nextflow
process hello {
"""
echo 'Hello world!'
"""
}
```
A process may define additional sections for *directives*, *inputs*, *outputs*, *script*, *exec*, and *stub*:
```nextflow
process greet {
// directives
errorStrategy 'retry'
tag { "${greeting}/${name}" }
input:
val greeting
val name
output:
stdout
script: // or exec:
"""
echo '${greeting}, ${name}!'
"""
stub:
"""
# do nothing
"""
}
```
- A process must define a script or exec section (see below). All other sections are optional. Directives do not have an explicit section label, but must be defined first.
- The `script:` section label can be omitted only when there are no other sections in the body.
- Sections must be defined in the order shown above, with the exception of the output section, which can also be specified after the script and stub.
Each section may contain one or more statements. For directives, inputs, and outputs, these statements must be [function calls](#function-call). See [Process reference][process-reference] for the set of available input qualifiers, output qualifiers, and directives.
The script section can be substituted with an exec section:
```nextflow
process greet {
input:
val greeting
val name
exec:
message = "${greeting}, ${name}!"
output:
val message
}
```
The script and stub sections must return a string in the same manner as a [function](#function).
See [Processes][process-page] for more information on the semantics of each process section.
### Process (typed)
A typed process is a process that uses static types for inputs and/or outputs:
```nextflow
process greet {
input:
greeting: String
name: String
stage:
env 'NAME', name
output:
stdout()
topic:
eval('bash --version') >> 'versions'
script:
"""
echo "${greeting}, \${NAME}!"
"""
}
```
Typed processes may specify the following sections:
##### `input:`
Consists of one or more process inputs. Each input has a name and type.
##### `stage:`
Consists of one or more stage directives. See [Inputs and outputs (typed)][inputs-and-outputs-typed] for the set of available stage directives.
##### `output:`
Consists of one or more *output statements*. An output statement can be a [variable name](#variable), an [assignment](#assignment), or an [expression statement](#expression-statement). An output statement must be the only output if it is an expression statement. See [Inputs and outputs (typed)][inputs-and-outputs-typed] for the set of available output functions.
##### `topic:`
Consists of one or more *topic statements*. A topic statement is a right-shift expression with an output value on the left side and a string on the right side.
:::note
Typed processes use the same behavior as legacy processes for all other sections.
:::
See [Typed processes][process-typed] for more information on the semantics of typed processes.
### Agent
An agent consists of a name and a body. The agent body may define sections for *directives*, *inputs*, *outputs*, and *prompt*:
```nextflow
agent qa {
// directives
model 'openai/gpt-5-mini'
instruction 'You are a concise scientific assistant.'
input:
question: String
output:
stdout()
prompt:
"""
Answer briefly: ${question}
"""
}
```
- An agent must define a prompt section. All other sections are optional. Directives do not have an explicit section label, but must be defined first.
- Sections must be defined in the order shown above.
- Agent inputs and outputs are declared in the same manner as a [typed process](#process-typed).
Agents may specify the following sections:
##### `input:`
Consists of one or more agent inputs. Each input has a name and type, or is a destructured record or tuple.
##### `output:`
Consists of a single *output statement*. An output with a name and type is answered by the model as structured output. An output may instead be an expression, which is composed from the agent's final answer (`stdout()`), output files (`file()`, `files()`), and inputs.
##### `prompt:`
Consists of one or more [statements](#statements) and must return a string in the same manner as a [function](#function). The returned string is the prompt sent to the model.
Directives must be [function calls](#function-call). See [Agents][agent-page] for the set of available directives and more information on the semantics of each agent section.
### Function
A function consists of a name, parameter list, and a body:
```nextflow
def greet(greeting, name) {
println "${greeting}, ${name}!"
}
```
Each parameter may declare a type, and the function may declare a return type after the parameter list:
```nextflow
def add(a: Integer, b: Integer) -> Integer {
a + b
}
```
The function body consists of one or more [statements](#statements).
The [return statement](#return) can be used to explicitly return from a function:
```nextflow
// return with no value
def greet(greeting, name) {
if( !greeting || !name )
return
println "${greeting}, ${name}!"
}
// return a value
def fib(x) {
if( x <= 1 )
return x
fib(x - 1) + fib(x - 2)
}
```
### Enum type
An enum type declaration consists of a name and a body. The body consists of a comma-separated list of identifiers:
```nextflow
enum Day {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
```
### Record type
A record type declaration consists of a name and a body. The body consists of one or more fields, where each field has a name and a type:
```nextflow
record FastqPair {
id: String
fastq_1: Path
fastq_2: Path
}
```
### Output block
The output block consists of one or more *output declarations*. Each output declaration consists of a name, an optional type, and one or more *output directives* that define how the output is published:
```nextflow
output {
samples: Channel {
path 'fastq'
index {
path 'index.csv'
}
}
summary_report: Path {
path '.'
}
}
```
Only one output block may be defined in a script. See [Workflow outputs][workflow-output-def] for the set of available output directives.
## Statements
Statements can be separated by either a newline or a semi-colon:
```nextflow
// newline
println 'Hello!'
println 'Hello again!'
// semi-colon
println 'Hello!' ; println 'Hello again!'
```
### Variable declaration
Variables can be declared with the `def` keyword:
```nextflow
def x = 42
```
Multiple variables can be declared in a single statement if the initializer is a tuple with the same number of elements as declared variables:
```nextflow
def (x, y) = tuple(1, 2)
```
A variable declaration may specify a type after the name, separated by a colon:
```nextflow
def x: Integer = 42
def (x: Integer, y: Integer) = tuple(1, 2)
```
### Assignment
An assignment statement consists of a *target* expression and a *source* expression separated by an equals sign:
```nextflow
v = 42
list[0] = 'first'
map.key = 'value'
```
The target expression must be a [variable](#variable), [index](#binary-expressions), or [property](#binary-expressions) expression. The source expression can be any expression.
Multiple variables can be assigned in a single statement when the source expression is a tuple with the same number of elements as assigned variables:
```nextflow
(x, y) = tuple(1, 2)
```
A *compound assignment* combines a binary operator with the equals sign. The following compound assignment operators are available: `+=`, `-=`, `*=`, `/=`, `%=`, `**=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, `>>>=`, `?=`. For example, `x += 1` is equivalent to `x = x + 1`:
```nextflow
x += 1
```
### Expression statement
Any [expression](#expressions) can be a statement.
In general, the only expressions that can have any effect as expression statements are function calls that have side effects (e.g. `println`) or an implicit return statement in a [function](#function) or [closure](#closure).
### assert
An assert statement consists of the `assert` keyword followed by a boolean expression, with an optional error message separated by a colon:
```nextflow
assert 2 + 2 == 4 : 'The math broke!'
```
### if/else
An if/else statement consists of an *if branch* and an optional *else branch*. Each branch consists of a boolean expression in parentheses, followed by either a single statement or a *block statement* (one or more statements in curly braces). For example:
```nextflow
def x = Math.random()
if( x < 0.5 ) {
println 'You lost.'
}
else {
println 'You won!'
}
```
If/else statements can be chained any number of times by making the else branch another if/else statement:
```nextflow
def grade = 89
if( grade >= 90 )
println 'You get an A!'
else if( grade >= 80 )
println 'You get a B!'
else if( grade >= 70 )
println 'You get a C!'
else if( grade >= 60 )
println 'You get a D!'
else
println 'You failed.'
```
### return
A return statement consists of the `return` keyword with an optional expression:
```nextflow
def add(a, b) {
return a + b
}
def sayHello(name) {
if( !name )
return
println "Hello, ${name}!"
}
```
A return statement can only be used in a [function](#function) or [closure](#closure). It cannot be used in a [workflow](#workflow) body.
### throw
A throw statement consists of the `throw` keyword followed by an expression that returns an error type:
```nextflow
throw new Exception('something failed!')
```
### try/catch
A try/catch statement consists of a *try block* followed by any number of *catch clauses*:
```nextflow
def text = null
try {
text = file('hello.txt').text
}
catch( e: IOException ) {
log.warn "Could not load hello.txt"
}
catch( e: FileNotFoundException | AccessDeniedException ) {
log.warn "Could not load hello.txt"
}
catch( e ) {
log.warn "Unexpected error: ${e}"
}
```
Each catch clause declares a variable and may specify any number of error types. Multiple error types can be specified by separating them with a pipe (`|`). A catch clause with no type catches any exception.
## Expressions
An expression represents a value. A *literal* value is an expression whose value is known at compile-time, such as a number, string, or boolean. All other expressions must be evaluated at run-time.
Every expression has a *type*, which may be resolved at compile-time or run-time.
### Variable
A variable expression is a reference to a variable or other named value:
```nextflow
def x = 42
x
// -> 42
```
### Number
A number literal can be an integer or floating-point number, and can be positive or negative. Integers can specified in binary with `0b`, octal with `0`, or hexadecimal with `0x`. Floating-point numbers can use scientific notation with the `e` or `E` prefix. Underscores can be used as thousands separators to make long numbers more readable.
```nextflow
// integer
42
-1
0b1001 // -> 9
031 // -> 25
0xabcd // -> 43981
// real
3.14
-0.1
1.59e7 // -> 15_900_000
1.59e-7 // -> 0.000000159
```
### Boolean
A boolean literal can be `true` or `false`:
```nextflow
assert true != false
assert !true == false
assert true == !false
```
### Null
The null literal is specified as `null`. It can be used to represent an "empty" value:
```nextflow
def x = null
x = 42
```
### String
A string literal consists of arbitrary text enclosed by single or double quotes:
```nextflow
println "I said 'hello'"
println 'I said "hello" again!'
```
A triple-quoted string can span multiple lines:
```nextflow
println '''
Hello,
How are you today?
'''
println """
We don't have to escape quotes anymore!
Even "double" quotes!
"""
```
A *slashy string* is enclosed by slashes instead of quotes:
```nextflow
/no escape!/
```
:::note
A slashy string cannot be empty because it would become a line comment.
:::
In single- and double-quoted strings (including their triple-quoted forms), the backslash character (`\`) escapes characters that would otherwise have special meaning, or inserts special characters:
| Escape | Character |
| ------ | --------- |
| `\b` | Backspace |
| `\t` | Tab (horizontal) |
| `\n` | Newline (line feed) |
| `\f` | Form feed |
| `\r` | Carriage return |
| `\s` | Space |
| `\"` | Double quote |
| `\'` | Single quote |
| `\\` | Backslash |
| `\$` | Dollar sign (prevents interpolation) |
| `\`*newline* | Line continuation (the newline is removed) |
| `\u`*HHHH* | Unicode character with the given hexadecimal code point (e.g. `é`) |
| `\`*NNN* | Character with the given octal value (e.g. `\101`) |
A backslash followed by any other character is a syntax error. To include a literal backslash, escape it as `\\`.
A slashy string does not interpret any of the above escapes, which makes it well-suited for regular expressions. The only exception is `\/`, which escapes a forward slash so that it does not terminate the string.
### Dynamic string
Double-quoted strings can be interpolated using the `${}` placeholder with an expression:
```nextflow
def names = ['Thing 1', 'Thing 2']
println "Hello, ${names.join(' and ')}!"
// -> Hello, Thing 1 and Thing 2!
```
If the expression is a name or simple property expression (one or more identifiers separated by dots), the curly braces can be omitted:
```nextflow
def name = [first: '', last: '']
println "Hello, $name.first $name.last!"
// -> Hello, !
```
Multi-line double-quoted strings can also be interpolated:
```nextflow
"""
blastp \
-in $input \
-out $output \
-db $blast_db \
-html
"""
```
### List
A list literal consists of a comma-separated list of zero or more expressions, enclosed in square brackets:
```nextflow
[1, 2, 3]
```
### Map
A map literal consists of a comma-separated list of one or more *map entries*, enclosed in square brackets. Each map entry consists of a *key expression* and *value expression* separated by a colon:
```nextflow
[alpha: 1, beta: 2, gamma: 3]
```
An empty map is specified with a single colon to distinguish it from an empty list:
```nextflow
[:]
```
Both the key and value can be any expression. Identifier keys are treated as string literals (i.e. the quotes can be omitted). A variable can be used as a key by enclosing it in parentheses:
```nextflow
def x = 'alpha'
[(x): 1]
// -> ['alpha': 1]
```
### Closure
A closure, also known as an anonymous function, consists of a parameter list followed by zero or more statements, enclosed in curly braces:
```nextflow
{ a, b -> a + b }
```
The above closure takes two arguments and returns their sum.
The closure body is identical to that of a [function](#function). Statements should be separated by newlines or semi-colons:
```nextflow
{ v ->
println 'Hello!'
println "We're in a closure!"
println 'Goodbye...'
v * v
}
```
See [Iterable\][stdlib-types-iterable] and [operator][operator-page] for more examples of how closures are used in practice.
### Index expression
An index expression consists of a *left expression* and a *right expression*, with the right expression enclosed in square brackets:
```nextflow
myList[0]
```
### Property expression
A property expression consists of an *object expression* and a *property*, separated by a *dot*, *spread dot*, or *safe dot*:
```nextflow
myFile.text // dot
myFiles*.text // spread dot: myFiles.collect { myFile -> myFile.text }
myFile?.text // safe dot: myFile != null ? myFile.text : null
```
The property must be an identifier or string literal.
### Function call
A function call consists of a name and argument list:
```nextflow
printf('Hello %s!\n', 'World')
```
A *method call* consists of an *object expression* and a function call separated by a *dot*, *spread dot*, or *safe dot*:
```nextflow
myFile.getText() // dot
myFiles*.getText() // spread dot: myFiles.collect { myFile -> myFile.getText() }
myFile?.getText() // safe dot: myFile != null ? myFile.getText() : null
```
The argument list may contain any number of *positional arguments* and *named arguments*:
```nextflow
file('hello.txt', checkIfExists: true)
```
The argument name must be an identifier or string literal.
The parentheses can be omitted when the function call is also an [expression statement](#expression-statement) and there is at least one argument:
```nextflow
// positional args
printf 'Hello %s!\n', 'World'
// positional and named args
file 'hello.txt', checkIfExists: true
// named args
resourceLabels group: 'my-group', user: 'me'
```
If the last argument is a closure, it can be specified outside of the parentheses:
```nextflow
// closure arg with additional args
[1, 2, 3].inject('result:') { acc, v -> acc + ' ' + v }
// single closure arg
[1, 2, 3].each() { v -> println v }
// single closure arg without parentheses
[1, 2, 3].each { v -> println v }
```
### Constructor call
A constructor call consists of the `new` keyword followed by a *type name* and an argument list enclosed in parentheses:
```nextflow
new java.util.Date()
```
If the type is implicitly available in the script, the *fully-qualified type name* can be elided to the *simple type name*:
```nextflow
new Date()
```
### Unary expressions
A unary expression consists of a *unary operator* followed by an expression:
```nextflow
!(2 + 2 == 4)
```
The following unary operators are available:
- `~`: bitwise NOT
- `!`: logical NOT
- `+`: unary plus
- `-`: unary minus
### Binary expressions
A binary expression consists of a *left expression* and a *right expression* separated by a *binary operator*:
```nextflow
2 + 2
```
The following binary operators are available:
- `**`: power (i.e. exponentiation)
- `*`: multiplication
- `/`: division
- `%`: remainder (i.e. modulo)
- `+`: addition
- `-`: subtraction
- `<<`: left shift
- `>>`: right shift
- `>>>`: unsigned right shift
- `..`: inclusive range
- `..<`: right-exclusive range
- `as`: type cast
- `instanceof`: type relation
- `!instanceof`: negated type relation
- `<`: less than
- `>`: greater than
- `<=`: less than or equals
- `>=`: greater than or equals
- `in`: membership
- `!in`: negated membership
- `==`: equals
- `!=`: negated equals
- `<=>`: spaceship (i.e. three-way comparison)
- `=~`: regex find
- `==~`: regex match
- `&`: bitwise AND
- `^`: bitwise XOR (exclusive or)
- `|`: bitwise OR
- `&&`: logical AND
- `||`: logical OR
- `?:` elvis (i.e. short ternary)
### Ternary expression
A ternary expression consists of a *test expression*, a *true expression*, and a *false expression*, separated by a question mark and a colon:
```nextflow
println x % 2 == 0 ? 'x is even!' : 'x is odd!'
```
### Parentheses
Any expression can be enclosed in parentheses:
```nextflow
1 + 2 * 3
// -> 1 + 6 -> 7
(1 + 2) * 3
// -> 3 * 3 -> 9
```
### Precedence
Compound expressions are evaluated in the following order:
- parentheses
- property expressions
- function calls
- index expressions
- `~`, `!`
- `**`
- `+`, `-` (unary)
- `*`, `/`, `%`
- `+`, `-` (binary)
- `<<`, `>>>`, `>>`, `..`, `..<`
- `as`
- `instanceof`, `!instanceof`
- `<`, `>`, `<=`, `>=`, `in`, `!in`
- `==`, `!=`, `<=>`
- `=~`, `==~`
- `&`
- `^`
- `|`
- `&&`
- `||`
- `?:` (ternary)
- `?:` (elvis)
[agent-page]: ../agent
[config-feature-flags]: ./feature-flags
[operator-page]: ./operator
[process-page]: ../process
[process-reference]: ./process
[inputs-and-outputs-typed]: ./process/inputs-outputs-typed
[process-typed]: ../process-typed
[stdlib-types-iterable]: ./stdlib-types/iterable
[workflow-output-def]: ../workflow#outputs
[workflow-typed-page]: ../workflow-typed
[pipeline-composition]: ../workflow-typed#pipeline-composition