Challenging Nextflow workflow - Need help please!

Hi everyone,

I’m encountering an issue while running my Nextflow workflow written in DSL2. The error suggests there’s an “Unknown process directive: path”. Below, I’ve included my workflow script and the error log for reference.

Nextflow Script:

#!/usr/bin/env nextflow

nextflow.enable.dsl=2

params.input_dir
params.output_dir
params.diameter_w0
params.diameter_w1
params.flow_threshold_w0
params.flow_threshold_w1

workflow {
    if (!params.input_dir || !params.output_dir || !params.diameter_w0 || !params.diameter_w1 || !params.flow_threshold_w0 || !params.flow_threshold_w1) {
        error "All parameters must be provided."
    }

    def (w0, w1) = processCellpose(
        params.input_dir,
        params.output_dir,
        params.diameter_w0,
        params.diameter_w1,
        params.flow_threshold_w0,
        params.flow_threshold_w1
    )

    w0.view { "w0 channel: $it" }
    w1.view { "w1 channel: $it" }

    processPlotting(
        w0,
        w1,
        params.input_dir,
        params.output_dir
    )
}

process processCellpose {
    container 'cellpose_env:latest'
    input:
        val input_dir
        val output_dir
        val diameter_w0
        val diameter_w1
        val flow_threshold_w0
        val flow_threshold_w1
    output:
        tuple(
            path("w0").optional(),
            path("w1").optional()
        )
    script:
    """
    python /app/cellpose_2ch.py \
        --input_dir $input_dir \
        --output_dir $PWD \
        --diameter_w0 $diameter_w0 \
        --diameter_w1 $diameter_w1 \
        --flow_threshold_w0 $flow_threshold_w0 \
        --flow_threshold_w1 $flow_threshold_w1
    mkdir -p ${output_dir}/w0
    mkdir -p ${output_dir}/w1
    mv w0/* ${output_dir}/w0/ || echo "No files in w0"
    mv w1/* ${output_dir}/w1/ || echo "No files in w1"
    """
}

process processPlotting {
    container 'plotting_env:latest'
    input:
        path w0
        path w1
        val input_dir
        val output_dir
    output:
        path("${output_dir}/results")
    script:
    """
    python /app/nuc_quant.py \
        --w0 $w0 \
        --w1 $w1 \
        --output_rois ${output_dir}/rois \
        --output_excel ${output_dir}/excel \
        --output_graphs ${output_dir}/graphs \
        --intensity_images $input_dir
    """
}

Error Log:

(base) arka@Dell:~/Desktop/Nextflow_projects/BR_nf$ cat .nextflow.log
Jan-28 22:15:39.686 [main] DEBUG nextflow.cli.Launcher - $> nextflow run workflow.nf --input_dir /home/arka/Desktop/Nextflow_projects/BR_nf/input_images --output_dir /home/arka/Desktop/Nextflow_projects/BR_nf/output --diameter_w0 20 --diameter_w1 11.9 --flow_threshold_w0 0.8 --flow_threshold_w1 0.6
Jan-28 22:15:39.835 [main] DEBUG nextflow.cli.CmdRun - N E X T F L O W  ~  version 24.10.4
Jan-28 22:15:39.862 [main] DEBUG nextflow.plugin.PluginsFacade - Setting up plugin manager > mode=prod; embedded=false; plugins-dir=/home/arka/.nextflow/plugins; core-plugins: nf-amazon@2.9.2,nf-azure@1.10.2,nf-cloudcache@0.4.2,nf-codecommit@0.2.2,nf-console@1.1.4,nf-google@1.15.3,nf-tower@1.9.3,nf-wave@1.7.4
Jan-28 22:15:39.886 [main] INFO  o.pf4j.DefaultPluginStatusProvider - Enabled plugins: []
Jan-28 22:15:39.888 [main] INFO  o.pf4j.DefaultPluginStatusProvider - Disabled plugins: []
Jan-28 22:15:39.892 [main] INFO  org.pf4j.DefaultPluginManager - PF4J version 3.12.0 in 'deployment' mode
Jan-28 22:15:39.911 [main] INFO  org.pf4j.AbstractPluginManager - No plugins
Jan-28 22:15:39.973 [main] DEBUG n.secret.LocalSecretsProvider - Secrets store: /home/arka/.nextflow/secrets/store.json
Jan-28 22:15:39.988 [main] DEBUG nextflow.secret.SecretsLoader - Discovered secrets providers: [nextflow.secret.LocalSecretsProvider@1bd81830] - activable => nextflow.secret.LocalSecretsProvider@1bd81830
Jan-28 22:15:40.060 [main] DEBUG nextflow.cli.CmdRun - Applied DSL=2 from script declaration
Jan-28 22:15:40.085 [main] DEBUG nextflow.cli.CmdRun - Launching `workflow.nf` [ridiculous_hoover] DSL2 - revision: 3a841c6dd5
Jan-28 22:15:40.087 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins default=[]
Jan-28 22:15:40.088 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins resolved requirement=[]
Jan-28 22:15:40.196 [main] DEBUG nextflow.Session - Session UUID: f0b82bc6-b354-40c8-a8e2-7f50728d7e1e
Jan-28 22:15:40.196 [main] DEBUG nextflow.Session - Run name: ridiculous_hoover
Jan-28 22:15:40.197 [main] DEBUG nextflow.Session - Executor pool size: 56
Jan-28 22:15:40.208 [main] DEBUG nextflow.file.FilePorter - File porter settings maxRetries=3; maxTransfers=50; pollTimeout=null
Jan-28 22:15:40.216 [main] DEBUG nextflow.util.ThreadPoolBuilder - Creating thread pool 'FileTransfer' minSize=10; maxSize=168; workQueue=LinkedBlockingQueue[10000]; allowCoreThreadTimeout=false
Jan-28 22:15:40.254 [main] DEBUG nextflow.cli.CmdRun - 
  Version: 24.10.4 build 5934
  Created: 20-01-2025 16:47 UTC (17:47 CEST)
  System: Linux 6.8.0-51-generic
  Runtime: Groovy 4.0.23 on OpenJDK 64-Bit Server VM 11.0.25+9-post-Ubuntu-1ubuntu122.04
  Encoding: UTF-8 (UTF-8)
  Process: 34911@Dell [127.0.1.1]
  CPUs: 56 - Mem: 125.8 GB (79.3 GB) - Swap: 2 GB (2 GB)
Jan-28 22:15:40.280 [main] DEBUG nextflow.Session - Work-dir: /home/arka/Desktop/Nextflow_projects/BR_nf/work [ext2/ext3]
Jan-28 22:15:40.281 [main] DEBUG nextflow.Session - Script base path does not exist or is not a directory: /home/arka/Desktop/Nextflow_projects/BR_nf/bin
Jan-28 22:15:40.294 [main] DEBUG nextflow.executor.ExecutorFactory - Extension executors providers=[]
Jan-28 22:15:40.309 [main] DEBUG nextflow.Session - Observer factory: DefaultObserverFactory
Jan-28 22:15:40.354 [main] DEBUG nextflow.cache.CacheFactory - Using Nextflow cache factory: nextflow.cache.DefaultCacheFactory
Jan-28 22:15:40.369 [main] DEBUG nextflow.util.CustomThreadPool - Creating default thread pool > poolSize: 57; maxThreads: 1000
Jan-28 22:15:40.499 [main] DEBUG nextflow.Session - Session start
Jan-28 22:15:41.283 [main] DEBUG nextflow.script.ScriptRunner - > Launching execution
Jan-28 22:15:41.438 [main] DEBUG nextflow.script.ScriptRunner - Parsed script files:
  Script_ffb0fc0169112fb4: /home/arka/Desktop/Nextflow_projects/BR_nf/workflow.nf
Jan-28 22:15:41.439 [main] DEBUG nextflow.Session - Session aborted -- Cause: Unknown process directive: `path`

Did you mean of these?
        each
Jan-28 22:15:41.445 [main] DEBUG nextflow.Session - 
Thread[Common-Cleaner,8,InnocuousThreadGroup]
  java.base@11.0.25/java.lang.Object.wait(Native Method)
  java.base@11.0.25/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155)
  java.base@11.0.25/jdk.internal.ref.CleanerImpl.run(CleanerImpl.java:148)
  java.base@11.0.25/java.lang.Thread.run(Thread.java:829)
  java.base@11.0.25/jdk.internal.misc.InnocuousThread.run(InnocuousThread.java:161)

Thread[Thread-1,5,main]
  java.base@11.0.25/jdk.internal.misc.Unsafe.park(Native Method)
  java.base@11.0.25/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:234)
  java.base@11.0.25/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2123)
  java.base@11.0.25/java.util.concurrent.LinkedBlockingDeque.pollFirst(LinkedBlockingDeque.java:513)
  java.base@11.0.25/java.util.concurrent.LinkedBlockingDeque.poll(LinkedBlockingDeque.java:675)
  app//nextflow.util.SimpleAgent.run(SimpleAgent.groovy:89)
  java.base@11.0.25/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  java.base@11.0.25/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  java.base@11.0.25/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  java.base@11.0.25/java.lang.reflect.Method.invoke(Method.java:566)
  app//org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
  app//groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
  app//groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1333)
  app//groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1088)
  app//groovy.lang.MetaClassImpl.invokeMethodClosure(MetaClassImpl.java:1017)
  app//groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1207)
  app//groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1088)
  app//groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
  app//groovy.lang.Closure.call(Closure.java:433)
  app//groovy.lang.Closure.call(Closure.java:412)
  app//groovy.lang.Closure.run(Closure.java:505)
  java.base@11.0.25/java.lang.Thread.run(Thread.java:829)

Thread[AnsiLogObserver,5,main]
  java.base@11.0.25/java.lang.Object.wait(Native Method)
  app//nextflow.trace.AnsiLogObserver.render0(AnsiLogObserver.groovy:185)
  java.base@11.0.25/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  java.base@11.0.25/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  java.base@11.0.25/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  java.base@11.0.25/java.lang.reflect.Method.invoke(Method.java:566)
  app//org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
  app//groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
  app//groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1333)
  app//groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1088)
  app//groovy.lang.MetaClassImpl.invokeMethodClosure(MetaClassImpl.java:1017)
  app//groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1207)
  app//groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1088)
  app//groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
  app//groovy.lang.Closure.call(Closure.java:433)
  app//groovy.lang.Closure.call(Closure.java:412)
  app//groovy.lang.Closure.run(Closure.java:505)
  java.base@11.0.25/java.lang.Thread.run(Thread.java:829)

Thread[main,5,main]
  java.base@11.0.25/java.lang.Thread.dumpThreads(Native Method)
  java.base@11.0.25/java.lang.Thread.getAllStackTraces(Thread.java:1653)
  app//nextflow.util.SysHelper.dumpThreads(SysHelper.groovy:188)
  app//nextflow.Session.abort(Session.groovy:800)
  app//nextflow.script.ScriptRunner.execute(ScriptRunner.groovy:149)
  app//nextflow.cli.CmdRun.run(CmdRun.groovy:376)
  app//nextflow.cli.Launcher.run(Launcher.groovy:503)
  app//nextflow.cli.Launcher.main(Launcher.groovy:658)

Thread[Finalizer,8,system]
  java.base@11.0.25/java.lang.Object.wait(Native Method)
  java.base@11.0.25/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:155)
  java.base@11.0.25/java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:176)
  java.base@11.0.25/java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:170)

Thread[Signal Dispatcher,9,system]

Thread[Reference Handler,10,system]
  java.base@11.0.25/java.lang.ref.Reference.waitForReferencePendingList(Native Method)
  java.base@11.0.25/java.lang.ref.Reference.processPendingReferences(Reference.java:241)
  java.base@11.0.25/java.lang.ref.Reference$ReferenceHandler.run(Reference.java:213)

Thread[process reaper,10,system]
  java.base@11.0.25/jdk.internal.misc.Unsafe.park(Native Method)
  java.base@11.0.25/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:234)
  java.base@11.0.25/java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:462)
  java.base@11.0.25/java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:361)
  java.base@11.0.25/java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:937)
  java.base@11.0.25/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1053)
  java.base@11.0.25/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1114)
  java.base@11.0.25/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  java.base@11.0.25/java.lang.Thread.run(Thread.java:829)

Jan-28 22:15:41.455 [main] ERROR nextflow.cli.Launcher - @unknown
nextflow.exception.IllegalDirectiveException: Unknown process directive: `path`

Did you mean of these?
        each
	at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
	at nextflow.script.ProcessConfig.checkName(ProcessConfig.groovy:239)
	at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
	at nextflow.script.ProcessConfig.methodMissing(ProcessConfig.groovy:258)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
	at groovy.lang.MetaClassImpl.invokeMissingMethod(MetaClassImpl.java:924)
	at groovy.lang.MetaClassImpl.invokePropertyOrMissing(MetaClassImpl.java:1413)
	at groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1335)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1088)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
	at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:645)
	at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:628)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeOnDelegationObjects(ClosureMetaClass.java:391)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:330)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
	at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
	at Script_ffb0fc0169112fb4$_runScript_closure2.doCall(Script_ffb0fc0169112fb4:54)
	at Script_ffb0fc0169112fb4$_runScript_closure2.doCall(Script_ffb0fc0169112fb4)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:279)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:412)
	at nextflow.script.ProcessDef.initialize(ProcessDef.groovy:110)
	at nextflow.script.ProcessDef.run(ProcessDef.groovy:168)
	at nextflow.script.BindableDef.invoke_a(BindableDef.groovy:51)
	at nextflow.script.ComponentDef.invoke_o(ComponentDef.groovy:40)
	at nextflow.script.WorkflowBinding.invokeMethod(WorkflowBinding.groovy:103)
	at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:651)
	at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:628)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeOnDelegationObjects(ClosureMetaClass.java:391)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:330)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
	at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
	at Script_ffb0fc0169112fb4$_runScript_closure1$_closure4.doCall(Script_ffb0fc0169112fb4:20)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:279)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1007)
	at groovy.lang.Closure.call(Closure.java:433)
	at groovy.lang.Closure.call(Closure.java:412)
	at nextflow.script.WorkflowDef.run0(WorkflowDef.groovy:204)
	at nextflow.script.WorkflowDef.run(WorkflowDef.groovy:188)
	at nextflow.script.BindableDef.invoke_a(BindableDef.groovy:51)
	at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
	at nextflow.script.BaseScript.run0(BaseScript.groovy:198)
	at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
	at nextflow.script.BaseScript.run(BaseScript.groovy:209)
	at nextflow.script.ScriptParser.runScript(ScriptParser.groovy:236)
	at nextflow.script.ScriptRunner.run(ScriptRunner.groovy:243)
	at nextflow.script.ScriptRunner.execute(ScriptRunner.groovy:138)
	at nextflow.cli.CmdRun.run(CmdRun.groovy:376)
	at nextflow.cli.Launcher.run(Launcher.groovy:503)
	at nextflow.cli.Launcher.main(Launcher.groovy:658)

Additional Details:

  1. Nextflow Version: 24.10.4
  2. Command Used to Run:
    nextflow run workflow.nf --input_dir /home/arka/Desktop/Nextflow_projects/BR_nf/input_images --output_dir /home/arka/Desktop/Nextflow_projects/BR_nf/output --diameter_w0 20 --diameter_w1 11.9 --flow_threshold_w0 0.8 --flow_threshold_w1 0.6
    
  3. Container Setup: I’m using cellpose_env:latest and plotting_env:latest containers.
  4. Expected Behavior: The script should execute processCellpose, produce outputs (w0 and w1), and pass them to processPlotting.

Issue:

The workflow fails with the error message: Unknown process directive: path. I’ve verified that the path directive is valid in DSL2. Could there be an issue with how I’m defining outputs or referencing path channels?

Any guidance on resolving this issue would be greatly appreciated; I am very new to nextflow and i am still learning.

Thanks in advance for your help! Let me know if further details are required.

Best regards,
Arka

I think it’s the way you are specifying optional outputs above. Marking an output as optional is shown as below:

    output:
        tuple path("w0"), path("w1"), optional: true

See docs here

Hi @Arkajyoti_Sarkar ! Welcome to Seqera Community Forum :slight_smile:

The first issue I see in your code is in the output block of the processCellpose process. See the snippet below:

    output:
        tuple(
            path("w0").optional(),
            path("w1").optional()
        )

I wonder from where you got an example like this. This is definitely not the way we write this block. The correct code follows below:

   output:                                                                        
        tuple path("w0"), path("w1"), optional: true

The first thing to realise is that if a you have a tuple as output, the tuple can be optional, or not. You can’t have part of a tuple being optional, so that’s why the optional option goes along the line, after the variables. I’d like to take this opportunity to remind you what it means to have an optional output: Normally, if a specified output is not produced by the task, the task will fail. Setting optional: true will cause the task to not fail, and instead emit nothing to the given output channel.

The second issue is that you seem to be confusing the Nextflow language with something else. Maybe Groovy. The following line doesn’t work as you think:

def (w0, w1) = processCellpose(

The processCellpose process returns a Nextflow channel and the only way you can handle Nextflow channels is through channel operators. You can’t unpack channels like this just because one or more of its elements is a tuple. You could use a channel operator such as map to do something like that, though.

The correct way to run this process and view what you want is:

   ....
   processCellpose(                                                                                                                                         
        params.input_dir,                                                       
        params.output_dir,                                                      
        params.diameter_w0,                                                     
        params.diameter_w1,                                                     
        params.flow_threshold_w0,                                               
        params.flow_threshold_w1                                                
    )                                                                           
                                                                                
    processCellpose.out.view { "w0: $it[0]\nw1: $it[1]" }

    ...

I can’t run your code as your instructions to pull these containers do not work, so I changed a bit the code of your process to:

workflow {                                                                      
    if (!params.input_dir || !params.output_dir || !params.diameter_w0 || !params.diameter_w1 || !params.flow_threshold_w0 || !params.flow_threshold_w1) {
        error "All parameters must be provided."                                
    }                                                                           
                                                                                
    processCellpose(                                                            
        params.input_dir,                                                       
        params.output_dir,                                                      
        params.diameter_w0,                                                     
        params.diameter_w1,                                                     
        params.flow_threshold_w0,                                               
        params.flow_threshold_w1                                                
    )                                                                           
                                                                                
    processCellpose.out.view { "w0: $it[0]\nw1: $it[1]" }
                                                                                
}                                                                               
                                                                                
process processCellpose {                                                       
    container 'cellpose_env:latest'                                             
    input:                                                                      
        val input_dir                                                           
        val output_dir                                                          
        val diameter_w0                                                         
        val diameter_w1                                                         
        val flow_threshold_w0                                                   
        val flow_threshold_w1                                                   
    output:                                                                     
        tuple path("w0"), path("w1"), optional: true                            
                                                                                
    script:                                                                     
    """                                                                         
    touch w0                                                                    
    touch w1                                                                    
  

Running like this, you’ll get the following:

Oh, one last thing. In your original code, when you view the channel you say something like “w0 channel” and “w1 channel”. This is a misunderstanding ow how it works. The processCellpose process has a single output channel that consists of elements that individually are tuples. So w0 and w1 are part of a channel element, not channels themselves. I recommend you read the docs and training materials on Nextflow channels so that this becomes clearer to you, as it’s very important to understand and master other Nextflow concepts.