{"record":{"id":"9dc093c01eb6e56b","repo":"tinyhumansai/openhuman","slug":"claude-code-driver-no-input-messages-to-deliver","errorCode":null,"errorMessage":"[claude-code][driver] no input messages to deliver","messagePattern":"\\[claude-code\\]\\[driver\\] no input messages to deliver","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/openhuman/inference/provider/claude_code/driver.rs","lineNumber":342,"sourceCode":"    );\n    if let Some(p) = mcp_config_path.as_ref() {\n        args.push(\"--mcp-config\".into());\n        args.push(p.display().to_string());\n        args.push(\"--strict-mcp-config\".into());\n    }\n    // Tool surface follows the permission posture: full access → no\n    // `--disallowedTools` (CC keeps its entire toolset incl. Bash/network);\n    // default `acceptEdits` → withhold the dangerous builtins (edits only).\n    if !full_access {\n        args.push(\"--disallowedTools\".into());\n        args.push(DISALLOWED_CC_BUILTINS.join(\",\"));\n    }\n\n    // Validate input *before* spawning so we don't launch a process we\n    // can't feed (CodeRabbit: validate before spawn).\n    let stdin_bytes = build_stdin(ctx.messages, is_new);\n    if stdin_bytes.is_empty() {\n        anyhow::bail!(\"[claude-code][driver] no input messages to deliver\");\n    }\n\n    log::debug!(\n        \"[claude-code][driver] spawn bin={} model={} is_new={} cc_session_id={}\",\n        ctx.bin_path.display(),\n        ctx.model,\n        is_new,\n        cc_session_id\n    );\n\n    // Best-effort: ensure the project dir exists so spawn (cwd) doesn't fail.\n    std::fs::create_dir_all(&ctx.project_dir).ok();\n\n    // Wrap the spawn in the macOS Seatbelt jail when available so CC's file\n    // writes are OS-confined: `sandbox-exec -p <profile> <claude> <args…>`.\n    #[cfg(target_os = \"macos\")]\n    let (program, final_args): (PathBuf, Vec<String>) = if jailed {\n        let profile = seatbelt_profile(&ctx.workspace_dir);","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/src/openhuman/inference/provider/claude_code/driver.rs#L324-L360","documentation":"The claude-code driver validates its serialized stdin payload BEFORE spawning the CLI (a deliberate validate-before-spawn check at driver.rs:~355). `build_stdin(ctx.messages, is_new)` returned empty, meaning the outgoing conversation has zero deliverable messages for this turn. Rather than launching a process it cannot feed, the driver refuses.","triggerScenarios":"Calling the claude-code chat path with an empty message list, a continuation turn (`is_new=false`) whose history resolved to nothing, or upstream filtering that stripped every message (e.g. all content blocks removed by sanitization), leaving `stdin_bytes` empty.","commonSituations":"An orchestration/agent loop passing an empty turn (empty user prompt after trimming), a thread-continuation bug that drops history, or a caller constructing a ChatRequest with no messages expecting the CLI to idle.","solutions":["Inspect the caller: ensure the ChatRequest/messages vector contains at least one non-empty message before dispatching to the claude-code provider.","If this is a continuation turn, verify the session history was loaded (cc_session_id resolves) so `build_stdin` has prior context to serialize.","Guard at the API edge: short-circuit empty turns with a user-facing 'empty message' response instead of invoking the provider.","Add a unit test pinning `build_stdin` output non-empty for your canonical message payload."],"exampleFix":"// before\nlet messages: Vec<ChatMessage> = vec![];\nlet resp = provider.chat(messages).await?; // trips the guard\n\n// after\nif messages.is_empty() {\n    return Ok(ChatResponse::empty(\"nothing to send\"));\n}\nlet resp = provider.chat(messages).await?;","handlingStrategy":"validation","validationCode":"// Before dispatching to the claude-code provider:\nlet deliverable: Vec<_> = messages.iter().filter(|m| !m.content_is_empty()).collect();\nif deliverable.is_empty() {\n    return Ok(empty_turn_response()); // skip the provider entirely\n}","typeGuard":"fn has_deliverable_messages(msgs: &[ChatMessage]) -> bool {\n    msgs.iter().any(|m| m.text().map(|t| !t.trim().is_empty()).unwrap_or(false))\n}","tryCatchPattern":null,"preventionTips":["Never construct a ChatRequest with an empty message vector.","For continuation turns, verify session history loaded before invoking the provider.","Add a unit test asserting build_stdin non-empty for your canonical payload."],"tags":["claude-code","validation","empty-input","precondition"],"backgroundTag":"empty-request-payload","analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}