windmill-labs/windmill · error · Error
${flag} '${tok}' matches multiple scripts (${matches.map((r)
Error message
${flag} '${tok}' matches multiple scripts (${matches.map((r) => r.path).sort().join(", ")}) — use the full path. What it means
Ambiguity guard in resolveScriptTokenOrThrow: the short name given to --upload or --arg resolves to multiple script nodes in the workspace graph (same basename under different folders), and the CLI needs exactly one to bind the argument to. The message lists every matching full path sorted, and the fix is to use a full path. The input at fault is the abbreviated script token.
Source
Thrown at cli/src/commands/pipeline/pipeline.ts:638
`/w/${workspace.workspaceId}/assets/graph?folder=${encodeURIComponent(f)}&asset_kinds=${ASSET_KINDS}`,
),
);
// Recover marker-only `data_upload`/`webhook`/`email` triggers the graph
// endpoint can't emit, so input-only entrypoints are cut here as they are
// under `--local` (else they'd auto-run empty).
await enrichDeployedNonAutorunTriggers(workspace.workspaceId, graph);
}
// Resolve a `--upload`/`--arg` script token to its graph node, with an
// actionable error when the short name is ambiguous or matches nothing.
const resolveScriptTokenOrThrow = (tok: string, flag: string): string => {
const id = resolveToken(graph, tok);
if (!id || !id.startsWith("script:")) {
const matches = graph.runnables.filter(
(r) => r.usage_kind === "script" && (r.path.split("/").pop() ?? r.path) === tok,
);
if (matches.length > 1) {
throw new Error(
`${flag} '${tok}' matches multiple scripts (${matches.map((r) => r.path).sort().join(", ")}) — use the full path.`,
);
}
throw new Error(`${flag} '${tok}' matched no script in f/${f}.`);
}
return id;
};
// `--upload <script>[:<param>]=<file|s3://key>` binds an object to a
// data_upload/webhook entry point so it becomes a runnable start (and its
// downstream is no longer cut) — the arg is injected at execution. Repeatable
// per script (accumulated) so a multi-S3Object entry can bind each param.
const boundBindingsByPath = new Map<string, UploadBinding[]>();
const boundNodeIds = new Set<string>();
for (const spec of opts.upload ?? []) {
const binding = parseUploadBinding(spec);
const id = resolveScriptTokenOrThrow(binding.scriptTok, "--upload");
const p = scriptPathOf(id);View on GitHub (pinned to e474e8803c)
Solutions
- Re-run using the full script path from the listed candidates, e.g. --arg folder/sub/script:param=value
- Rename one of the colliding scripts so short names are unique
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at cli/src/commands/pipeline/pipeline.ts:638 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/8bf0368910e48f03.
Report an issue: GitHub.