gitbutlerapp/gitbutler · error · anyhow::Error
line {line_number}: invalid pick commit: {err}
Error message
line {line_number}: invalid pick commit: {err} What it means
While parsing an edited interactive integration script, a `pick <commit>` line referenced a commit that resolve_commit could not match: the hash/prefix is not in the editable divergence set (not-integrated local-only commits plus all upstream-only commits), or the prefix matched multiple commits and is ambiguous. The inner error states which case.
Source
Thrown at crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs:56
continue;
};
let arguments = tokens.get(1..).unwrap_or_default();
let step = match command {
"pick" => {
if message_part.is_some() {
bail!("line {line_number}: pick does not accept a message clause");
}
if arguments.len() != 1 {
bail!("line {line_number}: pick requires exactly one commit");
}
let commit = arguments
.first()
.copied()
.expect("validated pick arity above");
InteractiveIntegrationStep::Pick {
commit_id: resolve_commit(commit, &allowed_commits).map_err(|err| {
anyhow::anyhow!("line {line_number}: invalid pick commit: {err}")
})?,
}
}
"merge" => {
if message_part.is_some() {
bail!("line {line_number}: merge does not accept a message clause");
}
if arguments.len() != 1 {
bail!("line {line_number}: merge requires exactly one commit");
}
let commit = arguments
.first()
.copied()
.expect("validated merge arity above");
InteractiveIntegrationStep::Merge {
commit_id: resolve_commit(commit, &allowed_commits).map_err(|err| {
anyhow::anyhow!("line {line_number}: invalid merge commit: {err}")
})?,View on GitHub (pinned to caf1f223d3)
Solutions
- Use only hashes printed in the generated script (the current divergence candidates)
- Lengthen an ambiguous short hash until it matches exactly one candidate
- Remove pick lines for commits already integrated upstream
- Regenerate the script from the current divergence and re-apply edits
Example fix
# before pick deadbee # not part of the editable divergence # after — copy the exact hash from the generated script pick 3333333
Defensive patterns
Strategy: validation
Validate before calling
// validate the edited script against the same divergence before executing
let candidates: HashSet<String> = divergence.local_only.iter()
.filter(|c| matches!(c.target_relation, IntegrationDivergenceTargetRelation::NotIntegrated))
.map(|c| c.id.to_string()).chain(divergence.upstream_only.iter().map(|c| c.id.to_string())).collect();
for token in extract_commit_tokens(&script) {
let matches = candidates.iter().filter(|c| c.starts_with(token)).count();
assert_eq!(matches, 1, "commit {token} unknown or ambiguous");
} Type guard
fn commit_resolves(candidates: &HashSet<String>, spec: &str) -> bool {
candidates.iter().filter(|c| c.starts_with(spec)).count() == 1
} Try / catch
match parse_integration_steps_script(&script, &divergence) {
Err(e) if e.to_string().contains("invalid pick commit") => { /* highlight the line for the user to fix the hash */ }
r => r,
} Prevention
- Only edit hashes the generated script already contains; never paste hashes from elsewhere
- Use full or sufficiently long hashes to avoid ambiguity errors
- Re-generate the script whenever the branch or upstream moves
When it happens
Trigger: parse_integration_steps_script receiving a script where a pick line references a commit outside the editable candidates — e.g. an already-integrated local commit, a random hash like deadbee, or a short prefix that matches two candidate hashes (ambiguity).
Common situations: User hand-edits the rebase-plan-style script and introduces/keeps a stale hash from an older divergence; long scripts edited after the divergence changed underneath.
Related errors
- line {line_number}: invalid merge commit: {err}
- line {line_number}: invalid squash commit '{commit}': {err}
- line {line_number}: invalid squash message: {err}
- Integration steps cannot be empty
- line {line_number}: pick does not accept a message clause
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/cfc9100d8e01fae2.
Report an issue: GitHub.