hatoo/oha · error · anyhow::Error

Invalid AWS credentials format. Expected…

Error message

Invalid AWS credentials format. Expected access_key:secret_key

What it means

When --aws-sigv4 is requested, run() derives AWS credentials from the --auth basic-auth string by splitting it on ':' and requiring exactly two parts: access_key and secret_key. If the string does not contain exactly one colon, the run aborts before any requests are made.

Solutions

  1. Provide --auth as exactly `access_key:secret_key` when using --aws-sigv4.
  2. Move any session token to the dedicated --aws-session-token option instead of appending it to --auth.
  3. Quote the argument so the shell does not mangle the colon-containing value.

Example fix

// before
oha --aws-sigv4 "aws:amz:us-east-1:s3" --auth AKIDEXAMPLE --aws-session-token TOK https://...
// after
oha --aws-sigv4 "aws:amz:us-east-1:s3" --auth AKIDEXAMPLE:secretKey --aws-session-token TOK https://...
Defensive patterns

Strategy: validation

Validate before calling

fn valid_aws_auth(auth: &str) -> bool {
    let parts: Vec<&str> = auth.split(':').collect();
    parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty()
}

Try / catch

match run(opts).await {
    Err(e) if e.to_string().contains("Invalid AWS credentials format") => {
        eprintln!("--auth must be access_key:secret_key (exactly one colon)");
    }
    Err(e) => return Err(e),
    Ok(v) => v,
}

Prevention

When it happens

Trigger: Passing --aws-sigv4 with an --auth value that splits into != 2 parts on ':', e.g. `--auth AKID` (no colon) or `--auth AKID:secret:extra` (two colons).

Common situations: Users forget that --auth doubles as AWS credential carrier when --aws-sigv4 is set; secret keys are fine but users paste `access:secret` plus a session token into the same string; shell expansion or quoting strips the colon.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of hatoo/oha@4efba2d113 (2026-09-09). Data as JSON: /api/errors/7d5d0f1e559afc36. Report an issue: GitHub.

Appendix: source

Thrown at src/main.rs:354

        help = "Number of native OS threads used by the async runtime (tokio). Defaults to the number of physical CPU cores.",
        long = "worker-threads",
        env = "TOKIO_WORKER_THREADS",
        default_value_t = std::num::NonZeroUsize::new(num_cpus::get_physical())
            .unwrap_or(std::num::NonZeroUsize::MIN)
    )]
    worker_threads: std::num::NonZeroUsize,
}

async fn run(mut opts: Opts) -> anyhow::Result<()> {
    let work_mode = opts.work_mode();
    let url = opts.url.expect("URL is required");

    // Parse AWS credentials from basic auth if AWS signing is requested
    let aws_config = if let Some(signing_params) = opts.aws_sigv4 {
        if let Some(auth) = &opts.basic_auth {
            let parts: Vec<&str> = auth.split(':').collect();
            if parts.len() != 2 {
                anyhow::bail!("Invalid AWS credentials format. Expected access_key:secret_key");
            }
            let access_key = parts[0];
            let secret_key = parts[1];
            let session_token = opts.aws_session.take();
            Some(AwsSignatureConfig::new(
                access_key,
                secret_key,
                &signing_params,
                session_token,
            )?)
        } else {
            anyhow::bail!("AWS credentials (--auth) required when using --aws-sigv4");
        }
    } else {
        None
    };

    let parse_http_version = |is_http2: bool, version: Option<&str>| match (is_http2, version) {

View on GitHub (pinned to 4efba2d113)