gitbutlerapp/gitbutler · error · anyhow::Error
line {line_number}: invalid squash message: {err}
Error message
line {line_number}: invalid squash message: {err} What it means
Only squash accepts an optional message clause, written after a '|' separator as `| message="..."` with a double-quoted string and only \n \r \t \" \\ escapes. parse_message_clause (or its quoted-string parser) rejected the clause: missing 'message=' prefix, missing/incorrect opening quote, unsupported escape, trailing characters after the closing quote, or an unterminated quote/escape.
Source
Thrown at crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs:97
bail!("line {line_number}: squash requires at least two commits");
}
let commits = arguments
.iter()
.map(|commit| {
resolve_commit(commit, &allowed_commits).map_err(|err| {
anyhow::anyhow!(
"line {line_number}: invalid squash commit '{commit}': {err}"
)
})
})
.collect::<Result<Vec<_>>>()?;
InteractiveIntegrationStep::Squash {
commits,
message: message_part
.map(parse_message_clause)
.transpose()
.map_err(|err| {
anyhow::anyhow!("line {line_number}: invalid squash message: {err}")
})?,
}
}
other => bail!("line {line_number}: unknown command '{other}'"),
};
steps.push(step);
}
Ok(steps)
}
fn render_step(step: &InteractiveIntegrationStep) -> String {
match step {
InteractiveIntegrationStep::Pick { commit_id } => format!("pick {}", short_id(*commit_id)),
InteractiveIntegrationStep::Merge { commit_id } => {
format!("merge {}", short_id(*commit_id))
}
InteractiveIntegrationStep::Squash { commits, message } => {View on GitHub (pinned to caf1f223d3)
Solutions
- Use the exact form: | message="your text" immediately after the commit list
- Escape only \n, \r, \t, \", \\ inside the quotes; put nothing after the closing quote
- Omit the clause entirely to keep the default squash message
Example fix
# before squash 3333333 4444444 | msg='combined fix' # wrong key and quotes # after squash 3333333 4444444 | message="combined fix"
Defensive patterns
Strategy: validation
Validate before calling
// validate the message clause shape before parsing the script
fn message_clause_ok(clause: &str) -> bool {
let Some(v) = clause.strip_prefix("message=") else { return false };
v.starts_with('"') && v.ends_with('"') && v.len() >= 2
&& !v[1..v.len()-1].matches('\\').any(|_| { /* escapes limited to n r t \ " — full check in parse_quoted_string */ false })
} Type guard
fn is_valid_message_clause(clause: &str) -> bool {
match clause.strip_prefix("message=") {
Some(v) => v.len() >= 2 && v.starts_with('"') && v.ends_with('"'),
None => false,
}
} Try / catch
if let Err(e) = parse_integration_steps_script(&script, &divergence) {
if e.to_string().contains("invalid squash message") { /* show expected format: | message="..." */ }
} Prevention
- Use the exact clause form `| message="..."` — key 'message=', double quotes, nothing trailing
- Escape only \n \r \t \" \\ inside the message; other backslash sequences are rejected
- Omit the clause when the default squash message is acceptable
When it happens
Trigger: Script lines like `squash 3333333 4444444 | msg="x"` (wrong key), `... | message='x'` (single quotes), `... | message="a\db"` (bad escape), or `... | message="a" trailing` (trailing text).
Common situations: Users writing rebase-style messages ('pick ... # message') instead of the pipe format; escaping quotes incorrectly when hand-editing.
Related errors
- line {line_number}: invalid squash commit '{commit}': {err}
- line {line_number}: invalid pick commit: {err}
- line {line_number}: invalid merge commit: {err}
- line {line_number}: squash requires at least two commits
- Squash step must have at least two commits
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/489c1b9ba82aaae4.
Report an issue: GitHub.