{"record":{"id":"6914978238e83cf4","repo":"gitbutlerapp/gitbutler","slug":"message-must-start-with-a-double-quote","errorCode":null,"errorMessage":"message must start with a double quote","messagePattern":"message must start with a double quote","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs","lineNumber":181,"sourceCode":"}\n\nfn split_message_clause(line: &str) -> Result<(&str, Option<&str>)> {\n    let Some((command_part, message_part)) = line.split_once('|') else {\n        return Ok((line, None));\n    };\n    Ok((command_part.trim_end(), Some(message_part.trim_start())))\n}\n\nfn parse_message_clause(clause: &str) -> Result<String> {\n    let value = clause\n        .strip_prefix(\"message=\")\n        .context(\"expected `message=` after `|`\")?;\n    parse_quoted_string(value)\n}\n\nfn parse_quoted_string(input: &str) -> Result<String> {\n    if !input.starts_with('\"') {\n        bail!(\"message must start with a double quote\");\n    }\n\n    let mut output = String::new();\n    let mut escaped = false;\n    for (index, ch) in input.char_indices().skip(1) {\n        if escaped {\n            let resolved = match ch {\n                '\\\\' => '\\\\',\n                '\"' => '\"',\n                'n' => '\\n',\n                'r' => '\\r',\n                't' => '\\t',\n                other => bail!(\"unsupported escape sequence '\\\\{other}'\"),\n            };\n            output.push(resolved);\n            escaped = false;\n            continue;\n        }","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs#L163-L199","documentation":"Raised by `parse_quoted_string` (via the squash message clause) when the text after `| message=` does not begin with a double quote. Message values are always double-quoted strings, e.g. `| message=\"combine fixes\"`.","triggerScenarios":"Lines like `squash 4f2a 991b | message=combine fixes` or `| message='single quoted'`. The `message=` prefix is stripped and the remainder must start with `\"`.","commonSituations":"Hand-writing the clause without quotes; using single quotes out of shell habit; a UI that inserts the raw message without quoting/escaping (instead of using the `quote_message` helper's format).","solutions":["Wrap the message in double quotes: `| message=\"combine fixes\"`.","Escape inner quotes/backslashes as `\\\"` and `\\\\`; only \\n, \\r, \\t escapes exist.","When generating scripts programmatically, reuse the same escaping rules as `quote_message` in parsing.rs."],"exampleFix":"# before\nsquash 4f2a9c1 991b3aa | message=combine fixes\n\n# after\nsquash 4f2a9c1 991b3aa | message=\"combine fixes\"","handlingStrategy":"validation","validationCode":"// Lint the message clause before parsing:\nif let Some((_, Some(msg))) = line.split_once('|') {\n    let m = msg.trim().strip_prefix(\"message=\").unwrap_or(\"\");\n    if !m.starts_with('\"') { warn(\"message must be double-quoted\"); }\n}","typeGuard":null,"tryCatchPattern":"if let Err(err) = parse_integration_steps_script(&script, &divergence) {\n    // message-quote errors carry 'line {n}:' context; surface verbatim in the editor\n}","preventionTips":["When a UI collects a squash message, always serialize it as | message=\"...\" with proper quoting (mirror quote_message).","Document that single quotes are not accepted."],"tags":["rust","git","but-workspace","integration","todo-script","parsing","quoting"],"backgroundTag":"unterminated-string-literal","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}