Hmbown/CodeWhale · error

operation_key cannot contain control characters

Error message

operation_key cannot contain control characters

What it means

validate_runtime_turn_operation_key character check (after the empty, length, and trim guards): the key contains control characters, which would corrupt idempotency-key matching in the durable binding store, so such keys are rejected.

Solutions

  1. Sanitize the key: filter out or replace control chars before calling
  2. Join multi-part data with a printable separator like ':' instead of newline
  3. Escape/encode raw-derived content (hex or base64) before embedding it in the key

Example fix

// before
let key = raw_output; // may contain \n
// after
let key: String = raw_output.chars().filter(|c| !c.is_control()).collect();
Defensive patterns

Strategy: validation

Validate before calling

let sanitized: String = key.chars().filter(|c| !c.is_control()).collect();
assert_eq!(sanitized, key, "operation_key must not contain control chars");

Prevention

When it happens

Trigger: Passing an operation_key containing chars like \n, \t, \r, \0, or other Unicode control codepoints, typically from binary data, raw process output, or multi-line heredocs.

Common situations: Building keys from command output or file contents that include newlines; programmatic key generation that accidentally joins with '\n'.

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 Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/af16b2f81a645afd. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:3508

    InvalidRequest,
    #[error("Turn operation acceptance is incomplete; retry lookup")]
    Incomplete,
    #[error("Turn operation lookup unavailable")]
    Unavailable,
}

fn validate_runtime_turn_operation_key(value: &str) -> Result<()> {
    if value.is_empty() {
        bail!("operation_key cannot be empty");
    }
    if value.len() > MAX_RUNTIME_TURN_OPERATION_KEY_BYTES {
        bail!("operation_key cannot exceed {MAX_RUNTIME_TURN_OPERATION_KEY_BYTES} UTF-8 bytes");
    }
    if value.trim() != value {
        bail!("operation_key cannot contain leading or trailing whitespace");
    }
    if value.chars().any(char::is_control) {
        bail!("operation_key cannot contain control characters");
    }
    Ok(())
}

fn validate_sha256_fingerprint(value: &str, label: &str) -> Result<()> {
    if value.len() != 64 || !value.bytes().all(|byte| byte.is_ascii_hexdigit()) {
        bail!("{label} must be a SHA-256 hex digest");
    }
    Ok(())
}

fn runtime_turn_operation_key_fingerprint(
    owner_id: &str,
    thread_id: &str,
    operation_key: &str,
) -> Result<String> {
    validate_runtime_turn_operation_key(operation_key)?;
    Ok(crate::hashing::sha256_hex(format!(

View on GitHub (pinned to 73e0f67d83)