GitoxideLabs/gitoxide · error

a pick is outside the editable history

Error message

a pick is outside the editable history

What it means

Validation inside `edit::todo::parse`. Each `pick` line's commit ID is checked against the set of commits that belong to the editable history; if the picked commit resolves to a commit outside that history (or an ID that cannot be resolved into it), parsing bails. This prevents the resulting rebase plan from silently grafting foreign commits into the edited branch. Fired by hand-edited todo files referencing hashes not part of the displayed history; fix by picking only commits listed in the editable range or removing the line.

Solutions

  1. Reference a commit ID inside the editable history
  2. Widen the plan scope to include the desired commit
  3. Delete the pick line if the commit should not be replayed

Example fix

// before
pick 9999999  # outside the edited range
// after
pick abc1234  # inside the edited range
Defensive patterns

Strategy: validation

Validate before calling

let id = repo.rev_parse_single(value)?;
if !scope.contains(&id) {
    anyhow::bail!("pick target {} is outside the editable history", id.shorten_or_id());
}

Type guard

fn pick_in_scope(id: gix::Id<'_>, scope: &HashSet<gix::oid::ObjectId>) -> bool {
    scope.contains(id.object_id())
}

Try / catch

match parse_plan(...) {
    Err(e) if e.to_string().contains("a pick is outside the editable history") => { /* correct the pick line */ }
    other => other?,
}

Prevention

When it happens

Trigger: `parse_plan` resolves a `pick <oid>` (or an abbreviated ID via `resolve_commit`) whose commit is not in the scope set.

Common situations: Picking a commit from another branch or from outside the forked range; typos or stale IDs copied from a previous rebase; abbreviated IDs that resolve to an unintended out-of-scope commit.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/aeff57fe1c19b5f3. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/todo.rs:974

        }
        let parent = cursor.context("the first todo command must follow a fork heading")?;
        let commit = match verb {
            "pick" => {
                let value = value.split_whitespace().next().context("a pick needs a commit ID")?;
                let resolved_id = state.resolved;
                let full_null = resolved_id.is_some_and(|id| {
                    value.len() == id.kind().len_in_bytes() * 2 && value.bytes().all(|byte| byte == b'0')
                });
                let (id, resolved) = if full_null {
                    (
                        resolved_id.context("a null pick has no materialized conflict state")?,
                        true,
                    )
                } else {
                    (resolve_commit(repo, value)?, false)
                };
                if !scope.contains(&id) {
                    anyhow::bail!("a pick is outside the editable history");
                }
                if picked.contains_key(&id) {
                    anyhow::bail!("a commit is picked more than once");
                }
                if resolved {
                    rebase::PlanCommit::Resolved(id)
                } else {
                    rebase::PlanCommit::Pick(id)
                }
            }
            "empty" => {
                let title = if value.trim().is_empty() { tail } else { value.trim() };
                if title.is_empty() {
                    anyhow::bail!("an empty commit needs a title");
                }
                rebase::PlanCommit::Empty(BString::from(title))
            }
            _ => anyhow::bail!("unsupported rebase todo command {verb:?}"),

View on GitHub (pinned to e73179060b)