windmill-labs/windmill · error · Error

Invalid s3 object ${JSON.stringify(s3Object)}: expected an s

Error message

Invalid s3 object ${JSON.stringify(s3Object)}: expected an s3://<storage>/<key> URI (e.g. "s3:///${s3Object}" for key "${s3Object}" in the default storage) or { s3: <key> }

What it means

parseS3Object normalizes an S3 object reference in typescript-client/s3Objects.ts:62, accepting either an s3://<storage>/<key> URI (s3:///<key> for default storage) or an object like { s3: <key> }. It throws this error when the input is neither a valid URI nor an object with an s3 key, e.g. a bare path or a malformed string.

Source

Thrown at typescript-client/s3Types.ts:62

/**
 * Parse an S3 object from URI string or record format
 * @param s3Object - S3 object as URI string (`s3://storage/key`, `s3:///key`
 *   for the default storage) or record. Any other string throws rather than
 *   falling back to an auto-generated key: an auto key is requested by
 *   omitting the object, and a fallback would silently misplace the upload
 *   on any typo.
 * @returns S3 object record with storage and s3 key
 */
export function parseS3Object(s3Object: S3Object): S3ObjectRecord {
  if (typeof s3Object === "object") return s3Object;
  const match = s3Object.match(/^s3:\/\/([^/]*)\/(.+)$/);
  if (match) return { storage: match[1] || undefined, s3: match[2] };
  if (s3Object.startsWith("s3://")) {
    throw new Error(
      `Invalid s3 object URI ${JSON.stringify(s3Object)}: expected s3://<storage>/<key> with a non-empty key (s3:///<key> for the default storage)`
    );
  }
  throw new Error(
    `Invalid s3 object ${JSON.stringify(s3Object)}: expected an s3://<storage>/<key> URI (e.g. "s3:///${s3Object}" for key "${s3Object}" in the default storage) or { s3: <key> }`
  );
}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Wrap a bare key into the default-storage URI: `s3:///${key}`
  2. Pass an object form instead: `{ s3: key }` with optional `storage`
  3. Check the string starts with `s3://` and has a non-empty key after `<storage>/` before calling
  4. Trim the input; ensure it is a string, not undefined/null

Example fix

// before
getPresignedS3PublicUrls(['reports/q3.json'])
// after
getPresignedS3PublicUrls(['s3:///reports/q3.json'])
Defensive patterns

Strategy: validation

Validate before calling

function isValidS3Object(o) {
  if (typeof o === 'string') return o.startsWith('s3://') && /^s3:\/\/([^/]*\/)?.+/.test(o);
  return o != null && typeof o === 'object' && typeof o.s3 === 'string' && o.s3.length > 0;
}

Type guard

function isS3ObjectInput(v: unknown): v is { s3: string; storage?: string } {
  return typeof v === 'object' && v !== null && 's3' in v && typeof (v as any).s3 === 'string';
}

Try / catch

try {
  const obj = parseS3Object(input);
} catch (e) {
  console.error('Bad S3 reference, expected s3://<storage>/<key> or { s3: key }:', input, e);
  throw new Error(`Invalid S3 reference: ${input}`);
}

Prevention

When it happens

Trigger: Calling parseS3Object (directly or via s3Obj or getPresignedS3PublicUrls) with a value that is not an object containing an `s3` field and not a string starting with `s3://` — e.g. passing `"myfile.json"` or `"s3:"` instead of `"s3:///myfile.json"` or `{ s3: "myfile.json" }`.

Common situations: Passing a bare S3 key where a URI is expected; building the URI by string concat and dropping the `s3://` prefix; reading a path from config/CLI args that was never normalized; extra whitespace or a storage prefix typo like `s3:/storage/key`.

Related errors


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