GitoxideLabs/gitoxide · error
the rebase todo has no fork heading
Error message
the rebase todo has no fork heading
What it means
The interactive-rebase todo parser in gix-tix requires every todo to begin with a `fork <commit-id>` separator heading, which anchors the first section of the plan. If the edited todo text, after stripping comments and blank lines, contains no fork heading at all, `parse` (called via `parse_plan`) aborts with this error. This is a structural validation of user-edited todo content, since the tool only allows editing history between fork points.
Solutions
- Reopen the todo and keep at least one `───── fork <commit-id> ─────` heading line above the first command.
- Ensure the fork heading is not commented out (it must not start with `# ` or be inside an `<!-- -->` block).
- If the edit was accidental, abort/cancel the todo edit and restart the command to regenerate a fresh todo.
Example fix
// before (todo file, no heading) pick 1a2b3c4d // after ────── fork 9f8e7d6c some base ────── pick 1a2b3c4d
Defensive patterns
Strategy: validation
Validate before calling
// Rust: check the edited todo keeps at least one fork heading
let has_fork = text.lines().any(|l| {
let l = l.trim();
!l.starts_with('#') && !l.is_empty() && l.starts_with('─') && l.trim_matches('─').trim().starts_with("fork ")
});
if !has_fork {
return Err("todo must keep at least one `fork <id>` heading");
} Try / catch
match parse_plan(&repo, text) {
Ok(plan) => apply(plan),
Err(e) if e.to_string().contains("no fork heading") => {
eprintln!("Todo edit removed the required fork heading; regenerating todo.");
regenerate_todo();
}
Err(e) => return Err(e),
} Prevention
- Never clear the whole editor buffer; delete only the lines you intend to change.
- Keep the `──── fork <id> ────` separators intact when pruning commands.
- Abort and rerun the command instead of hand-reconstructing a todo from scratch.
When it happens
Trigger: Calling `parse`/`parse_plan` on todo text where all `─ fork <id> ─` separator lines were deleted by the user in the editor, where the whole file was emptied, or where every remaining line is a comment or blank.
Common situations: A user clears the editor buffer or a misconfigured `$EDITOR`/script overwrites the todo file with empty or template-only content; commenting out everything including the fork heading with `#` or `<!-- -->`.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- the rebase todo contains more than one @ command
- the last fork section contains no commits
- an editable commit is not connected to the selected base
- a fork section contains no commits
- a fork target must be picked before it is used
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/58dac021dd297d0f.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/todo.rs:1015
}
steps.push(rebase::PlanStep {
parent,
commit,
squash: Vec::new(),
});
cursor = Some(rebase::PlanParent::Step(index));
section_last_step = Some(index);
if marked {
let target = rebase::PlanParent::Step(index);
if checkout_target.is_some_and(|checkout| checkout != target) {
anyhow::bail!("the @ command and @ reference point to different results");
}
checkout_target = Some(target);
}
section_has_commit = true;
}
if sections == 0 {
anyhow::bail!("the rebase todo has no fork heading");
}
if sections > 1 && !section_has_commit {
anyhow::bail!("the last fork section contains no commits");
}
if state.marker_required && checkout_target.is_none() {
anyhow::bail!("the current checkout marker must be retained");
}
let checkout_reference = match (checkout_target, explicit_checkout_reference) {
(Some(target), Some((name, reference_target))) => {
if target != reference_target {
anyhow::bail!("the @ command and @ reference point to different results");
}
Some(name)
}
(None, Some(_)) => anyhow::bail!("an @ reference requires an @ command at the same result"),
(Some(target), None) => state
.head_ref
.take()View on GitHub (pinned to e73179060b)