windmill-labs/windmill · error

Invalid S3 mode argument: {}

Error message

Invalid S3 mode argument: {}

What it means

In S3 mode, a SQL script's return/parameter type carries inline options as a space-separated list of `key=value` pairs (e.g. `s3:// prefix=foo storage=r2 format=parquet`). `parse_s3_mode` splits that string and raises this error when any token lacks an `=` separator, i.e. it is not a valid `key=value` argument.

Source

Thrown at backend/parsers/windmill-parser-sql/src/lib.rs:180

        Some(x) => x,
        None => return Ok(None),
    };
    let args_str = cap
        .get(1)
        .map(|x| x.as_str().to_string())
        .unwrap_or_default();

    let mut prefix = None;
    let mut storage = None;
    let mut format = S3ModeFormat::Json;

    for kv in args_str.split(' ').map(|kv| kv.trim()) {
        if kv.is_empty() {
            continue;
        }
        let mut it = kv.split('=');
        let (Some(key), Some(value)) = (it.next(), it.next()) else {
            return Err(anyhow!("Invalid S3 mode argument: {}", kv));
        };
        match (key.trim(), value.trim()) {
            ("prefix", _) => prefix = Some(value.to_string()),
            ("storage", _) => storage = Some(value.to_string()),
            ("format", "json") => format = S3ModeFormat::Json,
            ("format", "parquet") => format = S3ModeFormat::Parquet,
            ("format", "csv") => format = S3ModeFormat::Csv,
            ("format", format) => return Err(anyhow!("Invalid S3 mode format: {}", format)),
            (_, _) => return Err(anyhow!("Invalid S3 mode argument: {}", kv)),
        }
    }

    Ok(Some(S3ModeArgs { prefix, storage, format }))
}

pub fn parse_sql_blocks(code: &str, track_dollar_quotes: bool) -> Vec<&str> {
    let mut blocks = vec![];
    let mut last_idx = 0;

View on GitHub (pinned to e474e8803c)

Solutions

  1. Rewrite the s3-mode options as `key=value` pairs separated by single spaces: `prefix=x storage=y format=parquet`.
  2. Check the full message — the offending token is printed after the colon — and add the missing `=` or value.
  3. Ensure values don't contain unescaped spaces; avoid spaces in prefixes/storage names.
  4. Keep `format` limited to `json` or `parquet` (other keys/values have their own validation).

Example fix

// before
s3:// prefix format

// after
s3:// prefix=my-folder format=parquet
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate s3-mode options are all key=value before calling the parser
fn valid_s3_mode_opts(opts: &str) -> bool {
    opts.split(' ')
        .map(str::trim)
        .filter(|kv| !kv.is_empty())
        .all(|kv| kv.split_once('=').map_or(false, |(k, v)| !k.is_empty() && !v.is_empty()))
}

Try / catch

// Rust
match parse_s3_mode(args_str) {
    Ok(mode) => mode,
    Err(e) if e.to_string().contains("Invalid S3 mode argument") => {
        eprintln!("{e} — expected key=value pairs like 'prefix=x format=parquet'");
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `parse_s3_mode` (from `do_postgresql`, `do_mysql`, `do_bigquery`, `do_mssql`, `do_snowflake`, `do_oracledb` when the s3-mode extension is present) with an s3-mode argument string containing a bare token with no `=`, such as `s3:// prefix` or `format` without a value.

Common situations: Hand-editing the s3 type extension and dropping the `=` or the value (`format` instead of `format=parquet`); using spaces inside a value without quoting so it splits into bare tokens; typos like `prefix:foo`; copying an s3 mode string from docs with truncated options.

Related errors


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