Hmbown/CodeWhale · error

generated mobile fragment is a valid header

Error message

generated mobile fragment is a valid header

What it means

The mobile login handler builds a redirect Location header from a session fragment and calls `HeaderValue::from_str(...).expect(...)`. `from_str` only fails for bytes outside visible HTTP header range (control chars), so this expect asserts the internally generated URL never contains such characters. A panic means the location string (base URL/path/fragment construction) produced an invalid header value.

Solutions

  1. Audit where `location` is built and percent-encode or strip non-visible characters before insertion
  2. Validate the configured base URL at startup (reject CRLF/control chars)
  3. Replace expect with graceful handling that returns a 500 instead of panicking the handler
  4. Add a unit test that the fragment builder output passes HeaderValue::from_str

Example fix

// before
HeaderValue::from_str(&location).expect("generated mobile fragment is a valid header")
// after
HeaderValue::from_str(&location).unwrap_or_else(|_| {
    tracing::error!("invalid mobile location header");
    HeaderValue::from_static("/")
})
Defensive patterns

Strategy: validation

Validate before calling

// rust
fn is_valid_header_value(s: &str) -> bool {
    s.bytes().all(|b| (32..=126).contains(&b) || b == b'\t')
}
// call before building the response:
assert!(is_valid_header_value(&location));

Type guard

// rust
fn valid_header(s: &str) -> Option<HeaderValue> {
    HeaderValue::from_str(s).ok()
}

Try / catch

// rust
match HeaderValue::from_str(&location) {
    Ok(v) => response.headers_mut().insert(header::LOCATION, v),
    Err(e) => { tracing::error!("bad location header: {e}"); return StatusCode::INTERNAL_SERVER_ERROR.into_response(); }
}

Prevention

When it happens

Trigger: The generated mobile fragment URL contains control characters or non-visible bytes — e.g. a configured external base URL with a newline/CR, unvalidated query parameters interpolated into the fragment, or corrupted session data feeding the location.

Common situations: Misconfigured PUBLIC/base URL containing whitespace or CRLF, user-controlled fragments concatenated into the redirect target, template bugs introducing `%0A`/raw newlines.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/0f56fcd93ff14f84. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_api.rs:1402

    let session = match mobile_state.consume_bootstrap(&nonce, peer.ip()) {
        Ok(session) => session,
        Err(mobile::BootstrapError::NonLoopback) => {
            return secured_mobile_text(StatusCode::FORBIDDEN, "bootstrap unavailable");
        }
        Err(mobile::BootstrapError::Invalid | mobile::BootstrapError::Expired) => {
            return secured_mobile_text(StatusCode::UNAUTHORIZED, "bootstrap unavailable");
        }
    };

    let location = format!(
        "/mobile#request_proof={}&stream_ticket={}",
        session.request_proof, session.stream_ticket
    );
    let cookie = mobile::mobile_session_cookie(&session.session_cookie);
    let mut response = (StatusCode::SEE_OTHER, "").into_response();
    response.headers_mut().insert(
        header::LOCATION,
        HeaderValue::from_str(&location).expect("generated mobile fragment is a valid header"),
    );
    response.headers_mut().insert(
        header::SET_COOKIE,
        HeaderValue::from_str(&cookie).expect("generated mobile cookie is a valid header"),
    );
    secure_mobile_response(&mut response);
    response
}

async fn exchange_mobile_session(State(state): State<RuntimeApiState>, req: Request) -> Response {
    let Some(mobile_state) = state.mobile.as_ref() else {
        return mobile_not_found();
    };
    let Some(expected) = state.runtime_token.as_deref() else {
        return mobile_not_found();
    };
    if !auth::request_has_header_runtime_token(&req, expected) {
        return mobile_unauthorized();

View on GitHub (pinned to 433685b202)