Correct handler syntax and langserver

This topic is somewhat similar to Handlers and the language server - #3 by John, however the proposed solution in there actually does not work for me.

When I place the workflow.onComplete handler outside the workflow block I get an error from the langserver: Statements cannot be mixed with script declarations -- move statements into a process or workflow.
However, this configuration runs just fine.

If, on the other hand, I place the handler inside the workflow, it does make the langserver happy, but I get a runtime error ERROR ~ Failed to invoke workflow.onComplete event handler.

I’m using nextflow version 24.10.4 and neovim with the latest release from the langserver repo.

Here is minimal repoducing example.

process ECHO {
    script:
    """
    echo "Hello, everybody!"
    echo "I love nextflow <3"
    """
}

workflow {
    ECHO()

    // Uncomment this to see the runtime error.
    // workflow.onComplete {
    //     log.info "Pipeline completed at: $workflow.complete"
    //     log.info "Execution status: ${ workflow.success ? 'OK' : 'failed' }"
    // }
}

// Uncomment this to see the langserver error.
// workflow.onComplete {
//     log.info "Pipeline completed at: $workflow.complete"
//     log.info "Execution status: ${ workflow.success ? 'OK' : 'failed' }"
// }

PS: Both the linked thread and the examples in the docs use println instead of log.info. I prefer the latter as I actually get some outtput formatting bugs with println and I would like the timestamp to be in the logs.

The full log:

Summary
$ cat .nextflow.log
Feb-07 10:10:09.276 [main] DEBUG nextflow.cli.Launcher - $> nextflow run bih-pipeline/nextflow/bowtie2/reprod.nf
Feb-07 10:10:10.187 [main] DEBUG nextflow.cli.CmdRun - N E X T F L O W  ~  version 24.10.4
Feb-07 10:10:10.372 [main] DEBUG nextflow.plugin.PluginsFacade - Setting up plugin manager > mode=prod; embedded=false; plugins-dir=/data/cephfs-1/home/users/niza10_c/.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
Feb-07 10:10:10.484 [main] INFO  o.pf4j.DefaultPluginStatusProvider - Enabled plugins: []
Feb-07 10:10:10.486 [main] INFO  o.pf4j.DefaultPluginStatusProvider - Disabled plugins: []
Feb-07 10:10:10.490 [main] INFO  org.pf4j.DefaultPluginManager - PF4J version 3.12.0 in 'deployment' mode
Feb-07 10:10:10.589 [main] INFO  org.pf4j.AbstractPluginManager - No plugins
Feb-07 10:10:10.880 [main] DEBUG nextflow.config.ConfigBuilder - Found config base: /data/cephfs-1/work/groups/drosten/users/niza10_c/bt-nxf/bih-pipeline/nextflow/bowtie2/nextflow.config
Feb-07 10:10:10.886 [main] DEBUG nextflow.config.ConfigBuilder - Parsing config file: /data/cephfs-1/work/groups/drosten/users/niza10_c/bt-nxf/bih-pipeline/nextflow/bowtie2/nextflow.config
Feb-07 10:10:11.089 [main] DEBUG n.secret.LocalSecretsProvider - Secrets store: /data/cephfs-1/home/users/niza10_c/.nextflow/secrets/store.json
Feb-07 10:10:11.172 [main] DEBUG nextflow.secret.SecretsLoader - Discovered secrets providers: [nextflow.secret.LocalSecretsProvider@435ce306] - activable => nextflow.secret.LocalSecretsProvider@435ce306
Feb-07 10:10:11.376 [main] DEBUG nextflow.config.ConfigBuilder - Applying config profile: `standard`
Feb-07 10:10:14.878 [main] DEBUG nextflow.cli.CmdRun - Applied DSL=2 by global default
Feb-07 10:10:14.974 [main] DEBUG nextflow.cli.CmdRun - Launching `bih-pipeline/nextflow/bowtie2/reprod.nf` [prickly_torvalds] DSL2 - revision: 0ad283aaa6
Feb-07 10:10:14.977 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins default=[]
Feb-07 10:10:14.977 [main] DEBUG nextflow.plugin.PluginsFacade - Plugins resolved requirement=[]
Feb-07 10:10:15.273 [main] DEBUG nextflow.Session - Session UUID: 5c36fa06-eb10-468e-a870-4588005dc1fd
Feb-07 10:10:15.274 [main] DEBUG nextflow.Session - Run name: prickly_torvalds
Feb-07 10:10:15.275 [main] DEBUG nextflow.Session - Executor pool size: 8
Feb-07 10:10:15.286 [main] DEBUG nextflow.file.FilePorter - File porter settings maxRetries=3; maxTransfers=50; pollTimeout=null
Feb-07 10:10:15.293 [main] DEBUG nextflow.util.ThreadPoolBuilder - Creating thread pool 'FileTransfer' minSize=10; maxSize=24; workQueue=LinkedBlockingQueue[10000]; allowCoreThreadTimeout=false
Feb-07 10:10:15.478 [main] DEBUG nextflow.cli.CmdRun -
  Version: 24.10.4 build 5934
  Created: 20-01-2025 16:47 UTC (17:47 CEST)
  System: Linux 5.14.0-503.15.1.el9_5.x86_64
  Runtime: Groovy 4.0.23 on OpenJDK 64-Bit Server VM 17.0.11-internal+0-adhoc..src
  Encoding: UTF-8 (UTF-8)
  Process: 1815048@hpc-login-1 [172.16.96.17]
  CPUs: 8 - Mem: 62.8 GB (53.6 GB) - Swap: 0 (0)
Feb-07 10:10:15.584 [main] DEBUG nextflow.Session - Work-dir: /data/cephfs-1/work/groups/drosten/users/niza10_c/bt-nxf/work [ceph]
Feb-07 10:10:15.585 [main] DEBUG nextflow.Session - Script base path does not exist or is not a directory: /data/cephfs-1/work/groups/drosten/users/niza10_c/bt-nxf/bih-pipeline/nextflow/bowtie2/bin
Feb-07 10:10:15.674 [main] DEBUG nextflow.executor.ExecutorFactory - Extension executors providers=[]
Feb-07 10:10:15.687 [main] DEBUG nextflow.Session - Observer factory: DefaultObserverFactory
Feb-07 10:10:15.875 [main] DEBUG nextflow.cache.CacheFactory - Using Nextflow cache factory: nextflow.cache.DefaultCacheFactory
Feb-07 10:10:15.886 [main] DEBUG nextflow.util.CustomThreadPool - Creating default thread pool > poolSize: 9; maxThreads: 1000
Feb-07 10:10:16.195 [main] DEBUG nextflow.Session - Session start
Feb-07 10:10:17.085 [main] DEBUG nextflow.script.ScriptRunner - > Launching execution
Feb-07 10:10:17.482 [main] DEBUG nextflow.executor.ExecutorFactory - << taskConfig executor: null
Feb-07 10:10:17.482 [main] DEBUG nextflow.executor.ExecutorFactory - >> processorType: 'local'
Feb-07 10:10:17.490 [main] DEBUG nextflow.executor.Executor - [warm up] executor > local
Feb-07 10:10:17.576 [main] DEBUG n.processor.LocalPollingMonitor - Creating local task monitor for executor 'local' > cpus=8; memory=62.8 GB; capacity=8; pollInterval=100ms; dumpInterval=5m
Feb-07 10:10:17.579 [main] DEBUG n.processor.TaskPollingMonitor - >>> barrier register (monitor: local)
Feb-07 10:10:18.088 [main] DEBUG nextflow.Session - Workflow process names [dsl2]: ECHO
Feb-07 10:10:18.180 [main] WARN  nextflow.Session - There's no process matching config selector: ADAPTERREMOVAL
Feb-07 10:10:18.182 [main] WARN  nextflow.Session - There's no process matching config selector: BOWTIE2_BUILD
Feb-07 10:10:18.183 [main] WARN  nextflow.Session - There's no process matching config selector: BOWTIE2_ALIGN
Feb-07 10:10:18.183 [main] DEBUG nextflow.Session - Igniting dataflow network (1)
Feb-07 10:10:18.184 [main] DEBUG nextflow.processor.TaskProcessor - Starting process > ECHO
Feb-07 10:10:18.185 [main] DEBUG nextflow.script.ScriptRunner - Parsed script files:
  Script_0f1e10606fd66747: /data/cephfs-1/work/groups/drosten/users/niza10_c/bt-nxf/bih-pipeline/nextflow/bowtie2/reprod.nf
Feb-07 10:10:18.185 [main] DEBUG nextflow.script.ScriptRunner - > Awaiting termination
Feb-07 10:10:18.185 [main] DEBUG nextflow.Session - Session await
Feb-07 10:10:19.294 [Task submitter] DEBUG n.executor.local.LocalTaskHandler - Launch cmd line: /bin/bash -ue .command.run
Feb-07 10:10:19.374 [Task submitter] INFO  nextflow.Session - [41/bb3b9c] Submitted process > ECHO
Feb-07 10:10:19.477 [Task monitor] DEBUG n.processor.TaskPollingMonitor - Task completed > TaskHandler[id: 1; name: ECHO; status: COMPLETED; exit: 0; error: -; workDir: /data/cephfs-1/work/groups/drosten/users/niza10_c/bt-nxf/work/41/bb3b9c62d972d32176190f9c50eb03]
Feb-07 10:10:19.478 [Task monitor] DEBUG nextflow.util.ThreadPoolBuilder - Creating thread pool 'TaskFinalizer' minSize=10; maxSize=24; workQueue=LinkedBlockingQueue[10000]; allowCoreThreadTimeout=false
Feb-07 10:10:19.583 [main] DEBUG nextflow.Session - Session await > all processes finished
Feb-07 10:10:19.675 [Task monitor] DEBUG n.processor.TaskPollingMonitor - <<< barrier arrives (monitor: local) - terminating tasks monitor poll loop
Feb-07 10:10:19.675 [main] DEBUG nextflow.Session - Session await > all barriers passed
Feb-07 10:10:19.678 [main] DEBUG nextflow.util.ThreadPoolManager - Thread pool 'TaskFinalizer' shutdown completed (hard=false)
Feb-07 10:10:19.684 [main] ERROR nextflow.script.WorkflowMetadata - Failed to invoke `workflow.onComplete` event handler
java.lang.NullPointerException: Cannot get property 'complete' on null object
        at org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:92)
        at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
        at Script_0f1e10606fd66747$_runScript_closure2$_closure4$_closure5.doCall(Script_0f1e10606fd66747:21)
        at Script_0f1e10606fd66747$_runScript_closure2$_closure4$_closure5.doCall(Script_0f1e10606fd66747)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        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.WorkflowMetadata$_invokeOnComplete_closure3.doCall(WorkflowMetadata.groovy:428)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        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:422)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2394)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2379)
        at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2420)
        at nextflow.script.WorkflowMetadata.invokeOnComplete(WorkflowMetadata.groovy:426)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
        at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
        at groovy.lang.MetaClassImpl.doInvokeMethod(MetaClassImpl.java:1333)
        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.InvokerHelper.invokeMethodSafe(InvokerHelper.java:82)
        at nextflow.script.WorkflowMetadata$_closure2.doCall(WorkflowMetadata.groovy:286)
        at nextflow.script.WorkflowMetadata$_closure2.call(WorkflowMetadata.groovy)
        at groovy.lang.Closure.run(Closure.java:505)
        at nextflow.Session.shutdown0(Session.groovy:741)
        at nextflow.Session.destroy(Session.groovy:694)
        at nextflow.script.ScriptRunner.shutdown(ScriptRunner.groovy:260)
        at nextflow.script.ScriptRunner.execute(ScriptRunner.groovy:146)
        at nextflow.cli.CmdRun.run(CmdRun.groovy:376)
        at nextflow.cli.Launcher.run(Launcher.groovy:503)
        at nextflow.cli.Launcher.main(Launcher.groovy:658)
Feb-07 10:10:19.773 [main] DEBUG n.trace.WorkflowStatsObserver - Workflow completed > WorkflowStats[succeededCount=1; failedCount=0; ignoredCount=0; cachedCount=0; pendingCount=0; submittedCount=0; runningCount=0; retriesCount=0; abortedCount=0; succeedDuration=94ms; failedDuration=0ms; cachedDuration=0ms;loadCpus=0; loadMemory=0; peakRunning=1; peakCpus=1; peakMemory=0; ]
Feb-07 10:10:19.987 [main] DEBUG nextflow.cache.CacheDB - Closing CacheDB done
Feb-07 10:10:20.180 [main] DEBUG nextflow.util.ThreadPoolManager - Thread pool 'FileTransfer' shutdown completed (hard=false)
Feb-07 10:10:20.180 [main] DEBUG nextflow.script.ScriptRunner - > Execution complete -- Goodbye

You’re missing the = I think

workflow {
    ECHO()

    workflow.onComplete = {
        log.info "Pipeline completed at: $workflow.complete"
        log.info "Execution status: ${ workflow.success ? 'OK' : 'failed' }"
    }
}

Thank you!

I obviously didn’t read the other thread carefully enough.

Is there a part of the docs that explain why the syntax is sometimes assignment, onComplete = {, and onComplete { at other times?
There is no mentioned in the handler page.

The workflow handlers are in a bit of a transition period. In DSL2, it happens to work with or without the equals sign, no particular reason. With the strict syntax there is now only one way to do it, but ultimately I think we will try to find a more concise syntax, since it’s a bit silly to say workflow.onComplete when you’re already in a workflow.

For now we added a note about this to the vscode docs page: VS Code integration — Nextflow documentation

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.