windmill-labs/windmill · error · Error
${scriptPath} declares no S3Object parameter to bind --uploa
Error message
${scriptPath} declares no S3Object parameter to bind --upload to. What it means
Thrown by resolveUploadArgs when `--upload <token>=<source>` is given without naming a parameter, and the referenced script declares zero S3Object parameters for the CLI to bind to. The CLI tries to auto-select when exactly one S3Object param exists, but with none it cannot attach the uploaded file and aborts with this message.
Source
Thrown at cli/src/commands/pipeline/pipeline.ts:496
const ls = localScripts?.get(scriptPath);
if (!ls) {
throw new Error(`No local file for ${scriptPath} to infer its S3Object parameter.`);
}
schema = (await inferSchema(ls.language as any, ls.content, {}, scriptPath)).schema;
} else {
schema = (await wmill.getScriptByPath({ workspace: workspaceId, path: scriptPath })).schema;
}
return schema;
};
const args: Record<string, any> = {};
for (const binding of bindings) {
let param = binding.param;
if (!param) {
const params = s3ObjectParams(await loadSchema());
if (params.length === 1) param = params[0];
else if (params.length === 0) {
throw new Error(`${scriptPath} declares no S3Object parameter to bind --upload to.`);
} else {
throw new Error(
`${scriptPath} has multiple S3Object parameters (${params.join(", ")}) — pick one with --upload ${binding.scriptTok}:<param>=${binding.source}`,
);
}
}
if (param in args) {
throw new Error(`--upload binds ${scriptPath}:${param} more than once.`);
}
let obj: { s3: string; storage?: string };
if (binding.source.startsWith("s3://")) {
// Canonical `s3://<storage>/<key>`; keeps named storage (see parseS3Uri).
obj = parseS3Uri(binding.source);
} else {
const buf = await readFile(binding.source);
// Key scoped by script + param so distinct sources sharing a basename
// (across scripts or params) don't clobber each other in the store.
const key = devUploadKey(scriptPath, param, binding.source);View on GitHub (pinned to e474e8803c)
Solutions
- Add or restore an S3Object-typed parameter in the target script's signature and re-push it.
- Explicitly name the parameter with `--upload <tok>:<param>=<source>` if a compatible param exists but wasn't auto-detected.
- If the param was just added, re-pull/ensure the server has the updated schema before running.
- Point --upload at the correct script token if the S3Object param lives on a different step.
Example fix
// before (script signature)
export async function main(path: string) {...}
// after
import { S3Object } from "windmill-sdk";
export async function main(file: S3Object, path: string) {...} Defensive patterns
Strategy: validation
Validate before calling
const schema = await getScriptSchema(scriptPath);
const s3Params = Object.entries(schema?.properties ?? {})
.filter(([, p]: any[]) => p?.type === "S3Object" || p?.format === "s3object")
.map(([k]) => k);
if (s3Params.length === 0) throw new Error(`${scriptPath} has no S3Object param; use --upload <tok>:<param>=... or fix the script`); Try / catch
try {
await run({ upload: ["f=source"], /* ... */ });
} catch (e: any) {
if (e.message.includes("declares no S3Object parameter")) {
console.error(`${e.message} — add an S3Object param to the script or bind explicitly with --upload <tok>:<param>=...`);
}
throw e;
} Prevention
- Type file-consuming script params as S3Object (windmill-sdk) so auto-binding can find them.
- Re-push scripts after signature changes so the server schema matches what --upload expects.
- Bind explicitly with `--upload <tok>:<param>=<source>` whenever more than one script/param could match.
- Check the inferred schema (`wmill script show <path>`) before wiring --upload.
When it happens
Trigger: Running `wmill pipeline run --upload <scriptTok>:<source>` against a script whose JSON schema (inferred locally or fetched from the server) contains no parameter of type S3Object — e.g. the parameter is a plain string/path, the schema is empty, or the uploaded source targets the wrong script token.
Common situations: Script signature changed from S3Object to string and the run command wasn't updated; the --upload token points at the wrong entrypoint in the pipeline; schema not yet pushed to the server after adding the S3Object param; a hand-written script missing the s3_object import/typing.
Related errors
- result.substring(__RESULT_ERR_PREFIX.length)
- Invalid migration name '${name}': use only letters, digits,
- Unknown workspace dependencies file format: ${path}. Valid f
- Cannot push flow ${remotePath}: step(s) reference non-worksp
- Completed jobs file must contain an array of jobs
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/199784254d8aa4ec.
Report an issue: GitHub.