windmill-labs/windmill · error · Error
--arg binds ${p}:${b.param} more than once.
Error message
--arg binds ${p}:${b.param} more than once. What it means
Duplicate-binding guard for --arg in the pipeline run command: two or more --arg <script>:<param>=<value> bindings target the same script and param, so one would silently overwrite the other (later spreads win). The inputs at fault are the repeated --arg entries for that script:param pair.
Source
Thrown at cli/src/commands/pipeline/pipeline.ts:672
const binding = parseUploadBinding(spec);
const id = resolveScriptTokenOrThrow(binding.scriptTok, "--upload");
const p = scriptPathOf(id);
(boundBindingsByPath.get(p) ?? boundBindingsByPath.set(p, []).get(p)!).push(binding);
boundNodeIds.add(id);
}
// `--arg <script>:<param>=<value>` overlays a plain run arg on a script in
// the selection. Unlike `--upload` it does not make the script a runnable
// start — it only supplies a value if the script runs.
const plainArgsByPath = new Map<string, Record<string, unknown>>();
for (const spec of opts.arg ?? []) {
const b = parseArgBinding(spec);
const p = scriptPathOf(resolveScriptTokenOrThrow(b.scriptTok, "--arg"));
const merged = plainArgsByPath.get(p) ?? plainArgsByPath.set(p, {}).get(p)!;
// Object.hasOwn, not `in`: a param named like a prototype member
// (`toString`, `constructor`, …) must not trip the duplicate check.
if (Object.hasOwn(merged, b.param)) {
throw new Error(`--arg binds ${p}:${b.param} more than once.`);
}
merged[b.param] = b.value;
}
// `// macros` libraries are definition-only: their macros are injected into
// consuming DuckDB scripts at run time, so "running" one is a no-op preview.
// They are excluded from starts and from every selection below (they'd
// otherwise read as manual roots, since nothing triggers them).
const macroLibPaths = new Set(
graph.runnables
.filter((r) => r.usage_kind === "script" && (r.macros?.length ?? 0) > 0)
.map((r) => r.path),
);
// Non-runnable graph nodes: the macro libraries above, plus (under `--local`)
// any script node with no local file. The local graph surfaces macro-consumer
// nodes for lineage display — a DuckDB script that calls a macro but isn't a
// `// pipeline` member (so has no previewable content). They must never enter
// the run: as a manual root they'd be scheduled, and a preview would fail withView on GitHub (pinned to e474e8803c)
Solutions
- Keep only one --arg per script:param pair
- If two different params were intended, correct the param name in one binding
- If you need a composite value, pass it as one JSON value: --arg script:param='{\"a\":1}'
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at cli/src/commands/pipeline/pipeline.ts:672 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/1e18ca21f52962d2.
Report an issue: GitHub.