{"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":"critical","filePath":"crates/app-server/src/lib.rs","lineNumber":876,"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        runtime_bridge: Arc::new(Mutex::new(None)),\n        runtime_thread_map: Arc::new(Mutex::new(HashMap::new())),\n        stdio_thread_hints: Arc::new(Mutex::new(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":858,"sourceCodeEnd":894,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/app-server/src/lib.rs#L858-L894","documentation":"The app-server resolves its authentication token from AppServerOptions before binding. If an auth_token was explicitly configured but consists only of whitespace after trimming, resolve_auth_token bails with this error. An explicitly-set-but-empty token is treated as a misconfiguration that must fail loudly rather than silently degrading to no-auth or a generated token.","triggerScenarios":"Constructing AppServerOptions with auth_token: Some(\"\") or Some(\"   \") and starting the server; loading the token from a config file or environment variable that is present but blank.","commonSituations":"An env var like APP_SERVER_AUTH_TOKEN set to an empty string in a deployment manifest; a config template with `auth_token = \"\"` left unfilled; CI injecting a secret that resolved to empty.","solutions":["Provide a real non-empty token in AppServerOptions.auth_token","Fix the environment variable or config file so it contains the actual secret","If you truly want no authentication, explicitly set insecure_no_auth (loopback binds only) instead of an empty token","Unset/omit the auth_token field entirely so the server's default token resolution applies"],"exampleFix":"// before\nlet opts = AppServerOptions { auth_token: Some(\"\".into()), .. };\n// after\nlet opts = AppServerOptions { auth_token: Some(real_token), .. };","handlingStrategy":"validation","validationCode":"let auth_token = env::var(\"APP_SERVER_AUTH_TOKEN\").unwrap_or_default();\nif !auth_token.trim().is_empty() {\n    opts.auth_token = Some(auth_token);\n} // else leave unset so the server's default resolution applies","typeGuard":"fn has_token(t: &Option<String>) -> bool { t.as_deref().map_or(false, |s| !s.trim().is_empty()) }","tryCatchPattern":"match app_server::run(opts) {\n    Err(e) if e.to_string().contains(\"auth token cannot be empty\") => {\n        eprintln!(\"auth_token configured but blank; fix APP_SERVER_AUTH_TOKEN or set insecure_no_auth\");\n        std::process::exit(2);\n    }\n    r => r,\n}","preventionTips":["Trim and validate tokens at config-load time, not at server start","Never write `auth_token = \"\"` in config templates; omit the key instead","Verify secret injection in CI actually resolved to a non-empty value","Use insecure_no_auth explicitly (loopback only) rather than an empty token for local testing"],"tags":["rust","auth","config","app-server"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}