Hmbown/CodeWhale · error · anyhow::Error
app-server auth token cannot be empty
Error message
app-server auth token cannot be empty
What it means
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.
Source
Thrown at crates/app-server/src/lib.rs:688
Ok(AppState {
config_path,
config: Arc::new(RwLock::new(config)),
runtime: Arc::new(RwLock::new(runtime)),
registry,
auth_token,
stdio_bridge: Arc::new(Mutex::new(None)),
stdio_thread_hints: Arc::new(Mutex::new(HashMap::new())),
pending_user_input: Arc::new(Mutex::new(std::collections::HashMap::new())),
in_flight_turns: Arc::new(Mutex::new(HashMap::new())),
})
}
fn resolve_auth_token(options: &AppServerOptions) -> Result<Option<String>> {
let configured = options.auth_token.as_ref().map(|token| token.trim());
if let Some(token) = configured
&& token.is_empty()
{
bail!("app-server auth token cannot be empty");
}
let has_explicit_token = configured.is_some();
if options.insecure_no_auth {
if !options.listen.ip().is_loopback() {
bail!("refusing unauthenticated app-server bind on non-loopback address");
}
eprintln!("warning: app-server HTTP auth disabled by --insecure-no-auth");
return Ok(None);
}
if !has_explicit_token && !options.listen.ip().is_loopback() {
bail!(
"refusing non-loopback app-server bind without explicit auth token; pass --auth-token or set CODEWHALE_APP_SERVER_TOKEN"
);
}
let token = configuredView on GitHub (pinned to 0c42157ee5)
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
Example fix
// server, before: console.log(JSON.stringify(hugePayload)); // server, after: console.error(JSON.stringify(hugePayload)); // logs to stderr // stdout.write(JSON.stringify(smallEnvelope) + '\n');
Defensive patterns
Strategy: try-catch
Validate before calling
// Smoke-test the server before wiring it into the client:
// spawn it, send an initialize request, and assert the first stdout line
// is newline-terminated, parses as JSON, and is well under 16 MiB.
async fn server_speaks_framed_json(cmd: &str) -> bool {
// spawn cmd; write initialize; read one line with read_line_capped-like cap;
// return line.ends_with(b'\n') && serde_json::from_slice::<serde_json::Value>(&line).is_ok()
true
} Try / catch
match transport.run().await {
Err(e) if e.to_string().contains("exceeded") && e.kind() == std::io::ErrorKind::InvalidData => {
// mark the server transport dead; surface a config error (stdout framing broken),
// do not retry the same oversized read
}
other => other,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Stdio transport read error: {err} {stderr}
- MCP server '{server_name}' has no command configured
- MCP server '{server}': process closed stdin before answering
- MCP server '{server}': {method} timed out after {timeout:?}
- MCP server '{server}': process closed stdout before answerin
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/c048fda5fe175d9c.
Report an issue: GitHub.