From 56314f98e4e41c62a1b3a780dc7d77a7d3c8c120 Mon Sep 17 00:00:00 2001 From: jorgee Date: Thu, 11 Jun 2026 11:05:14 +0200 Subject: [PATCH 1/2] Fix "No such variable" in typed output with job arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When job array batching is enabled (process `array` directive), the parent `TaskArrayRun` is created with a fresh, empty task context — it is only a launcher that dispatches its child tasks and stages no outputs of its own. However `TaskBean` calls `getOutputFilesNames()` for every task, including the array parent. For typed (v2) processes this evaluates the process-level declared output patterns against the task context, and a pattern that references an input variable (e.g. `file("${localFile.baseName}_sce.rds")`) fails with `No such variable: localFile` because the parent context has no inputs bound. DSL v1 was unaffected because it reads the task's instance-resolved outputs, which are empty for the parent. Override `getOutputFilesNames()` in `TaskArrayRun` to return an empty list, matching the effective v1 behaviour and avoiding evaluation of output declarations against the parent's empty context. Fixes https://github.com/nextflow-io/nextflow/issues/7215 Signed-off-by: Jorge Ejarque Signed-off-by: jorgee --- .../nextflow/processor/TaskArrayRun.groovy | 13 +++++++++++++ .../nextflow/processor/TaskArrayRunTest.groovy | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy b/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy index b99875c895..47d7d979a8 100644 --- a/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy @@ -58,4 +58,17 @@ class TaskArrayRun extends TaskRun { return true } + /** + * A job array is only a launcher for its child tasks and does not stage + * any output files of its own. Returning an empty list also avoids + * resolving the process output declarations against the array task + * context, which (unlike a child task) has no input variables bound and + * would otherwise fail for typed outputs that reference an input + * (see https://github.com/nextflow-io/nextflow/issues/7215). + */ + @Override + List getOutputFilesNames() { + return Collections.emptyList() + } + } diff --git a/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy b/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy index 3baa7b6213..1594896414 100644 --- a/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy @@ -20,6 +20,7 @@ import nextflow.Session import nextflow.container.resolver.ContainerInfo import nextflow.executor.Executor import nextflow.executor.TaskArrayExecutor +import nextflow.script.ProcessConfigV2 import spock.lang.Specification /** * @@ -52,4 +53,20 @@ class TaskArrayRunTest extends Specification { task.isArray() } + // see https://github.com/nextflow-io/nextflow/issues/7215 + def 'should not stage output files because it is only a child task launcher' () { + given: 'a typed (v2) process so that getOutputFilesNames would take the v2 path' + def config = Mock(ProcessConfigV2) + def processor = Mock(TaskProcessor) { getConfig() >> config } + and: 'an array task whose context has no input variables bound, like the real array parent' + def task = new TaskArrayRun(processor: processor, context: Mock(TaskContext)) + + when: + def names = task.getOutputFilesNames() + + then: 'no output files are returned and the v2 output declarations are never evaluated' + names == [] + 0 * config.getOutputs() + } + } From ea331bc508aee2931ec95761eb890c68cd9d7830 Mon Sep 17 00:00:00 2001 From: Ben Sherman Date: Thu, 11 Jun 2026 11:14:05 -0500 Subject: [PATCH 2/2] cleanup Signed-off-by: Ben Sherman --- .../main/groovy/nextflow/processor/TaskArrayRun.groovy | 8 -------- .../groovy/nextflow/processor/TaskArrayRunTest.groovy | 5 ++--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy b/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy index 47d7d979a8..c68eeea6d0 100644 --- a/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/processor/TaskArrayRun.groovy @@ -58,14 +58,6 @@ class TaskArrayRun extends TaskRun { return true } - /** - * A job array is only a launcher for its child tasks and does not stage - * any output files of its own. Returning an empty list also avoids - * resolving the process output declarations against the array task - * context, which (unlike a child task) has no input variables bound and - * would otherwise fail for typed outputs that reference an input - * (see https://github.com/nextflow-io/nextflow/issues/7215). - */ @Override List getOutputFilesNames() { return Collections.emptyList() diff --git a/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy b/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy index 1594896414..daec2e4720 100644 --- a/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/processor/TaskArrayRunTest.groovy @@ -53,9 +53,8 @@ class TaskArrayRunTest extends Specification { task.isArray() } - // see https://github.com/nextflow-io/nextflow/issues/7215 def 'should not stage output files because it is only a child task launcher' () { - given: 'a typed (v2) process so that getOutputFilesNames would take the v2 path' + given: 'a typed process' def config = Mock(ProcessConfigV2) def processor = Mock(TaskProcessor) { getConfig() >> config } and: 'an array task whose context has no input variables bound, like the real array parent' @@ -64,7 +63,7 @@ class TaskArrayRunTest extends Specification { when: def names = task.getOutputFilesNames() - then: 'no output files are returned and the v2 output declarations are never evaluated' + then: 'no output files are returned and the typed output declarations are never evaluated' names == [] 0 * config.getOutputs() }