Skip to content

Convert a typed process input to its declared type in process entry mode - #7384

Open
pditommaso wants to merge 1 commit into
masterfrom
fix-typed-input-numeric-coercion
Open

Convert a typed process input to its declared type in process entry mode#7384
pditommaso wants to merge 1 commit into
masterfrom
fix-typed-input-numeric-coercion

Conversation

@pditommaso

Copy link
Copy Markdown
Member

Problem

In process entry mode (nextflow run script.nf --param value with no workflow, and nextflow module run), a param is mapped to a typed (V2) process input by parsing the text into whatever it looks like rather than into the type the process declared. On current master:

nextflow.enable.types = true

process qc_verdict {
    input:
    n50_kb: Integer
    completeness_pct: Double

    output:
    verdict: String = stdout()

    script:
    """
    awk -v n50='${n50_kb}' -v comp='${completeness_pct}' 'BEGIN { printf "%s", (n50 >= 40 && comp >= 95.0 ? "PASS" : "WARN") }'
    """
}
$ nextflow run qc.nf --n50_kb 45 --completeness_pct 96.4
WARN: [qc_verdict] invalid argument type at index 1 -- expected a Double but got a String

Four distinct failures in ProcessEntryHandler.getValueForInputV2:

declared value master produces
Double "96.4" (CLI text) String — no branch handles Double at all
Float "40" Integer — the integer branch is tried first for Integer and Float
Double 96.4 (a BigDecimal, e.g. a value supplied programmatically) BigDecimal — non-CharSequence values return early, unconverted
Integer "abc" String, silently — the task receives abc

The type == Float test appears twice (ProcessEntryHandler.groovy:387,393) and Double never appears, which is the root of the first two.

Fix

Convert the value to the declared type:

  • Numeric declared types go through one path: parse text as a decimal number first (Groovy's cast does not parse strings into numbers), then cast to the declaration.
  • String/Character use a plain cast.
  • A value that cannot be converted now fails up front with the same message the record/collection path already produces (Parameter --n50 with type Integer cannot be assigned to abc [String]) instead of reaching the task as the wrong type.
  • An integral declared type rejects fractional text (--n50 3.7 on an Integer) rather than silently truncating.

Boolean still accepts only the explicit true/false spellings — never Groovy truthiness, so --flag yes is passed through unchanged as before — and Path still resolves with its existence check. Both now also accept an already-typed value, which previously short-circuited on the value !instanceof CharSequence early return.

The V1 (untyped) path and its meta.yml-driven coercion are untouched.

Verification

End to end, same command on both trees:

# master
WARN: [qc_verdict] invalid argument type at index 1 -- expected a Double but got a String
# this branch
(no warning; the task body receives a Double)

TestsProcessEntryHandlerTest gains 21 cases (12 conversions, 4 rejections, 5 boolean/path). 15 of them fail without the production change; all 50 pass with it.

Regression sweepnextflow.script.*, nextflow.module.*, nextflow.cli.module.*, nextflow.processor.*: 1130 tests with the same 7 pre-existing failures as an untouched master, measured on both trees (CmdModuleRunTest 3, RegistryClientFactoryTest 2, TaskInputResolverTest 1, BaseScriptTest 1). No class got worse.

Note on the behaviour change

Two inputs that previously reached the task as the wrong type now raise an error instead:

  • --n50 abc on an Integer input (previously the String abc was passed to the task)
  • --n50 3.7 on an Integer input (previously the String 3.7 was passed)

Both were user errors that surfaced later as a confusing warning or a task failure, so failing up front with the param name and the declared type seems the better contract — but say the word if you'd rather keep them lenient.

🤖 Generated with Claude Code

`nextflow run` in process entry mode (and `nextflow module run`) mapped a param
to a typed (V2) process input by parsing the text into "whatever it looks
like" rather than into the type the process declared. Consequences:

- A `Double` input was not handled at all -- neither branch matched, so the raw
  String reached the task: `--completeness_pct 96.4` on a `completeness_pct:
  Double` input logs `invalid argument type at index 1 -- expected a Double but
  got a String` and the task body receives a String.
- A `Float` input given integral text got an Integer (the integer branch is
  tried first for both Integer and Float), so the same warning fires.
- A non-string value supplied programmatically was passed through untouched: a
  JSON number is a BigDecimal for `96.4` and an Integer for `40`, so a `Double`
  input receives the wrong Number.
- An invalid value was accepted silently: `--n50 abc` on an `Integer` input
  passed the String `abc` to the task.

Convert the value to the DECLARED type instead. Numeric types go through a
single path that parses text as a decimal number (Groovy's cast does not parse
strings into numbers) and then casts to the declaration; `String`/`Character`
use a plain cast. A value that cannot be converted now fails up front with the
same message the record/collection path already produces, rather than reaching
the task as the wrong type. An integral declared type rejects fractional text
(`--n50 3.7` on an `Integer`) instead of silently truncating it.

Boolean keeps accepting only the explicit `true`/`false` spellings (never
Groovy truthiness) and Path keeps resolving with the existence check; both now
also accept an already-typed value, which previously short-circuited earlier.

Verified end to end: on master, `nextflow run qc.nf --n50_kb 45
--completeness_pct 96.4` against a typed process warns `expected a Double but
got a String`; with this change the warning is gone and the task receives a
Double. Tests: 12 conversion cases, 4 rejection cases and 5 boolean cases in
ProcessEntryHandlerTest (15 of them fail without this change). The
nextflow.script.*, nextflow.module.*, nextflow.cli.module.* and
nextflow.processor.* suites show the same 7 pre-existing failures as an
untouched master (CmdModuleRunTest 3, RegistryClientFactoryTest 2,
TaskInputResolverTest 1, BaseScriptTest 1).

Assisted-by: Claude Code (Opus 5)
Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com>
@netlify

netlify Bot commented Jul 25, 2026

Copy link
Copy Markdown

Deploy Preview for nextflow-docs canceled.

Name Link
🔨 Latest commit dfe24de
🔍 Latest deploy log https://app.netlify.com/projects/nextflow-docs/deploys/6a646bb9d01d8c00087df21c

@pditommaso
pditommaso requested review from bentsherman and jorgee July 25, 2026 07:56
@bentsherman

Copy link
Copy Markdown
Member

I think we need to write out the mapping rules for CLI params -> Nextflow types as part of #7208 . Then we can just test them all as in this PR. I also want to see if I can unify the params mapping logic for params block / process entry / workflow entry.

I will try to finalize an ADR and test suite as part of #7208 before we make any more quick fixes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants