{"record":{"id":"c048fda5fe175d9c","repo":"Hmbown/CodeWhale","slug":"app-server-auth-token-cannot-be-empty","errorCode":null,"errorMessage":"app-server auth token cannot be empty","messagePattern":"app-server auth token cannot be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/app-server/src/lib.rs","lineNumber":688,"sourceCode":"    Ok(AppState {\n        config_path,\n        config: Arc::new(RwLock::new(config)),\n        runtime: Arc::new(RwLock::new(runtime)),\n        registry,\n        auth_token,\n        stdio_bridge: Arc::new(Mutex::new(None)),\n        stdio_thread_hints: Arc::new(Mutex::new(HashMap::new())),\n        pending_user_input: Arc::new(Mutex::new(std::collections::HashMap::new())),\n        in_flight_turns: Arc::new(Mutex::new(HashMap::new())),\n    })\n}\n\nfn resolve_auth_token(options: &AppServerOptions) -> Result<Option<String>> {\n    let configured = options.auth_token.as_ref().map(|token| token.trim());\n    if let Some(token) = configured\n        && token.is_empty()\n    {\n        bail!(\"app-server auth token cannot be empty\");\n    }\n    let has_explicit_token = configured.is_some();\n\n    if options.insecure_no_auth {\n        if !options.listen.ip().is_loopback() {\n            bail!(\"refusing unauthenticated app-server bind on non-loopback address\");\n        }\n        eprintln!(\"warning: app-server HTTP auth disabled by --insecure-no-auth\");\n        return Ok(None);\n    }\n\n    if !has_explicit_token && !options.listen.ip().is_loopback() {\n        bail!(\n            \"refusing non-loopback app-server bind without explicit auth token; pass --auth-token or set CODEWHALE_APP_SERVER_TOKEN\"\n        );\n    }\n\n    let token = configured","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/app-server/src/lib.rs#L670-L706","documentation":"The stdio MCP transport reads the server's stdout line by line through read_line_capped with a hard cap (MAX_MCP_RESPONSE_BYTES, 16 MiB in mcp/wire.rs). If a single line accumulates more than max bytes without ever seeing a newline, the read aborts with InvalidData so a misbehaving server cannot turn read_line into unbounded memory use.","triggerScenarios":"The MCP server writes one JSON-RPC message larger than 16 MiB with no newline (a giant embedded base64 resource or tool result), or it emits non-line-delimited output on stdout - binary data, a spinner/progress renderer, or logs printed to stdout instead of stderr.","commonSituations":"Servers that embed whole files in a single response; servers logging to stdout and breaking JSON-RPC framing; a wrong command configured so a non-MCP program's output is parsed as protocol traffic.","solutions":["Make the server log to stderr only; stdout must carry newline-terminated JSON-RPC frames","Page or chunk large payloads (resource URIs with offsets) instead of one giant message","Verify the configured command actually speaks MCP stdio: run it manually and confirm it emits one JSON object per line","If you control both ends and truly need larger frames, raise MAX_MCP_RESPONSE_BYTES in your fork"],"exampleFix":"// server, before: console.log(JSON.stringify(hugePayload));\n// server, after: console.error(JSON.stringify(hugePayload)); // logs to stderr\n//                     stdout.write(JSON.stringify(smallEnvelope) + '\\n');","handlingStrategy":"try-catch","validationCode":"// Smoke-test the server before wiring it into the client:\n// spawn it, send an initialize request, and assert the first stdout line\n// is newline-terminated, parses as JSON, and is well under 16 MiB.\nasync fn server_speaks_framed_json(cmd: &str) -> bool {\n    // spawn cmd; write initialize; read one line with read_line_capped-like cap;\n    // return line.ends_with(b'\\n') && serde_json::from_slice::<serde_json::Value>(&line).is_ok()\n    true\n}","typeGuard":null,"tryCatchPattern":"match transport.run().await {\n    Err(e) if e.to_string().contains(\"exceeded\") && e.kind() == std::io::ErrorKind::InvalidData => {\n        // mark the server transport dead; surface a config error (stdout framing broken),\n        // do not retry the same oversized read\n    }\n    other => other,\n}","preventionTips":["Route MCP server logs to stderr; stdout carries only newline-delimited JSON-RPC","Chunk or page large payloads instead of single giant messages","Smoke-test new servers manually before adding them to config"],"tags":["mcp","stdio","json-rpc","size-limit","invalid-data"],"backgroundTag":"message-size-limit-exceeded","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}