windmill-labs/windmill · error · Error

No local file for ${scriptPath} to infer its S3Object parame

Error message

No local file for ${scriptPath} to infer its S3Object parameter.

What it means

Thrown by loadSchema in the `wmill pipeline run --upload` path when the entrypoint script is expected to be present among locally pulled scripts but is not found in the localScripts map. With --local, the CLI infers the script's parameter schema from the local file content to find an S3Object parameter; without the local file it cannot proceed.

Source

Thrown at cli/src/commands/pipeline/pipeline.ts:480

// Resolve one `--upload` binding to the run-arg it injects: pick the target
// S3Object parameter (explicit `:param`, else the script's sole S3Object arg)
// and turn the source into an object key — an `s3://` source is used as-is, a
// local path is uploaded to the workspace store under a deterministic dev key.
async function resolveUploadArgs(
  bindings: UploadBinding[],
  scriptPath: string,
  workspaceId: string,
  local: boolean | undefined,
  localScripts: Map<string, LocalScript> | undefined,
): Promise<Record<string, any>> {
  // Fetch the schema at most once, and only when a binding omits `:param`.
  let schema: any | undefined;
  const loadSchema = async (): Promise<any> => {
    if (schema) return schema;
    if (local) {
      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(

View on GitHub (pinned to e474e8803c)

Solutions

  1. Run `wmill pipeline pull` in the project root to refresh local script state, then retry.
  2. Verify the script path/token in --upload matches an existing local file under the pulled layout.
  3. Drop --local to resolve the schema from the deployed script on the server instead of local files.
  4. Check you are in the directory that was pulled (wmill.yaml present) and the same workspace.

Example fix

// before: stale local state
wmill pipeline run f/pipeline --local --upload s:file=./data.csv

// after
wmill pipeline pull
wmill pipeline run f/pipeline --local --upload s:file=./data.csv
Defensive patterns

Strategy: validation

Validate before calling

if (local && !localScripts.has(scriptPath)) {
  throw new Error(`Run \`wmill pipeline pull\` first — ${scriptPath} is not in local state`);
}

Try / catch

try {
  await params({ local: true, /* ... */ });
} catch (e: any) {
  if (e.message.includes("No local file for")) {
    console.error(`${e.message} — re-pull with \`wmill pipeline pull\` or drop --local`);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running `wmill pipeline run --local --upload <tok>:<param>=<source>` where the referenced script path has no corresponding entry in the local pull state — e.g. the script was deleted or renamed locally, the path token doesn't match any pulled script, or --local was passed in a directory that was never pulled.

Common situations: Editing a pulled pipeline and renaming the entrypoint file without re-pulling; running from the wrong directory (no .windmill/pull state); workspace switch so local state belongs to another workspace; a stale pull where the server-side script is newer.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/53ace9ca3bf2b6b0. Report an issue: GitHub.