GitoxideLabs/gitoxide · error

resolution present if ID can be used as fallback

Error message

resolution present if ID can be used as fallback

What it means

When printing submodule info, the head commit ID is used as a fallback to resolve a describe name; `try_resolve()` is expected to succeed whenever an ID is available. The `.expect()` panics if name resolution fails despite a usable head commit ID.

Solutions

  1. Run `git -C <submodule> fsck` / `git fetch` in the submodule to restore missing commit objects.
  2. Initialize/update submodules (`git submodule update --init`) so their HEADs resolve.
  3. Replace the `.expect()` with a graceful fallback (print the raw OID) instead of panicking.

Example fix

// before
.expect("resolution present if ID can be used as fallback")
.format_with_dirty_suffix(dirty_suffix.map(ToOwned::to_owned))?

// after
match repo.head_commit()?.try_resolve() {
    Some(desc) => desc.format_with_dirty_suffix(dirty_suffix.map(ToOwned::to_owned))?.to_string(),
    None => commit_id.to_string(),
}
Defensive patterns

Strategy: fallback

Validate before calling

// verify submodule head resolves before describe
if repo.head_id().is_err() {
    return "no resolvable HEAD".into();
}

Try / catch

match repo.head_commit()?.try_resolve() {
    Some(desc) => desc.format_with_dirty_suffix(dirty_suffix.map(ToOwned::to_owned))?.to_string(),
    None => commit_id.to_string(),
}

Prevention

When it happens

Trigger: Running submodule listing where a submodule worktree exists with a resolvable head commit, but `repo.head_commit()?.try_resolve()` returns None or resolution otherwise fails.

Common situations: Corrupted or shallow submodule repos where the head commit object is missing; detached heads in submodules with broken refs; repositories missing objects after interrupted fetches.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at gitoxide-core/src/repository/submodule.rs:58

        path = sm.path()?,
        config = if state.superproject_configuration {
            "config:yes"
        } else {
            "config:no"
        },
        head_id = submodule_short_hash(sm.head_id()?, sm_repo.as_ref()),
        index_id = submodule_short_hash(sm.index_id()?, sm_repo.as_ref()),
        worktree = match sm_repo {
            Some(repo) => {
                // TODO(name-revision): this is the simple version, `git` gives it
                //                      multiple tries https://github.com/git/git/blob/fac96dfbb1c24369ba7d37a5affd8adfe6c650fd/builtin/submodule--helper.c#L161
                //                      and even uses `git name-rev`/`git describe --contains` which we can't do yet.
                repo.head_commit()?
                    .describe()
                    .names(SelectRef::AllRefs)
                    .id_as_fallback(true)
                    .try_resolve()?
                    .expect("resolution present if ID can be used as fallback")
                    .format_with_dirty_suffix(dirty_suffix.map(ToOwned::to_owned))?
                    .to_string()
            }
            None => {
                "no worktree".into()
            }
        },
        url = sm.url()?.to_bstring()
    )?;
    Ok(())
}

fn submodule_short_hash(id: Option<gix::ObjectId>, repo: Option<&Repository>) -> String {
    id.map_or_else(
        || "none".to_string(),
        |id| repo.map_or_else(|| id.to_string(), |repo| id.attach(repo).shorten_or_id().to_string()),
    )
}

View on GitHub (pinned to e73179060b)