{"record":{"id":"f7e106c2358672b1","repo":"zeroclaw-labs/zeroclaw","slug":"commit-message-cannot-be-empty","errorCode":null,"errorMessage":"Commit message cannot be empty","messagePattern":"Commit message cannot be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/git_operations.rs","lineNumber":506,"sourceCode":"        for line in &trimmed_lines {\n            if line.is_empty() {\n                consecutive_blanks += 1;\n                if consecutive_blanks <= 2 {\n                    sanitized_lines.push(line);\n                }\n            } else {\n                consecutive_blanks = 0;\n                sanitized_lines.push(line);\n            }\n        }\n        // Drop trailing blank lines.\n        while sanitized_lines.last().is_some_and(|l: &&str| l.is_empty()) {\n            sanitized_lines.pop();\n        }\n        let sanitized = sanitized_lines.join(\"\\n\");\n\n        if sanitized.is_empty() {\n            anyhow::bail!(\"Commit message cannot be empty\");\n        }\n\n        // Limit message length\n        let message = Self::truncate_commit_message(&sanitized);\n\n        let output = self\n            .run_git_command(&[\"commit\", \"-m\", &message], working_dir)\n            .await;\n\n        match output {\n            Ok(_) => Ok(ToolResult {\n                success: true,\n                output: format!(\"Committed: {message}\").into(),\n                error: None,\n            }),\n            Err(e) => Ok(ToolResult {\n                success: false,\n                output: ToolOutput::default(),","sourceCodeStart":488,"sourceCodeEnd":524,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/git_operations.rs#L488-L524","documentation":"The git commit tool extracts the 'message' string from its JSON args and sanitizes it: lines are trim_end'ed, leading and trailing blank lines are dropped, and runs of more than 2 blank lines are collapsed. If nothing survives that sanitization (sanitized.is_empty()), the tool bails with 'Commit message cannot be empty' before ever invoking git. This guard exists because `git commit -m` with an empty message either fails cryptically or, with --allow-empty-message, creates a commit with no subject. Note that a missing 'message' key is a different error ('Missing 'message' parameter'); this one fires only when the key exists but is blank after trimming.","triggerScenarios":"Calling the git tool's commit operation with args like {\"message\": \"\"}, {\"message\": \"   \"}, or {\"message\": \"\\n\\n\\n\"}. Any message whose lines are all empty after trim_end (spaces, tabs, bare newlines) reaches the bail at git_operations.rs:506.","commonSituations":"An LLM agent emits a commit tool call with a placeholder or empty message; a caller templates the message from a variable that is unset and renders to whitespace; a UI forwards an unedited, blank commit dialog; a script chains git_add then git_commit but forgets to pass the message through.","solutions":["Supply a non-blank commit message, e.g. {\"message\": \"fix: handle empty stash index\"} — a single subject line is enough.","If the message is built from a variable or template, check it after trimming and substitute a default subject before calling the tool.","Pre-validate in the caller: reject or re-prompt when message.lines().all(|l| l.trim().is_empty()) so the tool is never invoked with blank input.","Remember the tool also truncates the message to 2000 chars, so put the important summary on the first line."],"exampleFix":"// before\nlet args = serde_json::json!({ \"message\": \"   \\n\\n\" });\n// tool bails: Commit message cannot be empty\n\n// after\nlet args = serde_json::json!({ \"message\": \"fix: correct stash index handling\" });","handlingStrategy":"validation","validationCode":"fn build_commit_args(message: &str) -> Option<serde_json::Value> {\n    let has_content = message.lines().any(|l| !l.trim().is_empty());\n    has_content.then(|| serde_json::json!({ \"message\": message }))\n}","typeGuard":null,"tryCatchPattern":"match tool_result {\n    Err(e) if e.to_string().contains(\"Commit message cannot be empty\") => {\n        // re-prompt for a subject; never retry with the same blank input\n    }\n    other => other,\n}","preventionTips":["Treat a blank commit message as a caller-side bug: assert on it in tests that build commit tool calls.","When templating messages, coalesce blank render results to a default subject like 'chore: automated change'.","Log the sanitized message length (not content) before invoking commit so blank input is visible in traces."],"tags":["git","commit","validation","zeroclaw-tools","agent-tools"],"backgroundTag":"empty-required-parameter","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}