GitoxideLabs/gitoxide · error

pattern still present

Error message

pattern still present

What it means

`Match::to_outer` resolves a stored pattern index against the Outcome's arena with `out.patterns.resolve(self.pattern).expect("pattern still present")`. Match results hold indices into the Outcome's pattern arena, so resolution can only fail if the Outcome (or the arenas backing it) was dropped/moved while matches referencing it outlive or bypass it — a lifetime/interning invariant.

Solutions

  1. Keep match results used only within the scope of the Outcome that produced them
  2. Never store/resolve match indices across different collection instances
  3. Upgrade gix to get lifetime-bound APIs if you were working around them unsafely
  4. Report with a minimal repro if hit through safe public APIs

Example fix

// before
let matches = { let outcome = do_search(); outcome.matches() }; // outcome dropped
resolve(matches)
// after
let outcome = do_search();
let matches = outcome.matches(); // use while outcome is alive
resolve(matches)
Defensive patterns

Strategy: type-guard

Type guard

fn matches_alive<'a>(m: &[gix_attributes::search::Match<'a>], _out: &'a Outcome) -> bool { true } // rely on the borrow checker instead of unsafe

Try / catch

std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| match.to_outer(&outcome)));

Prevention

When it happens

Trigger: Using match results after the underlying search Outcome that owns the pattern storage is no longer intact, or mixing Match values from different Outcome instances; typically prevented by the borrow-bound API, so violations indicate unsafe code or version skew.

Common situations: Storing `crate::search::Match` beyond the Outcome's lifetime via unsafe transmutes, or custom code that re-resolves indices against a rebuilt collection.

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/d1e6afcc0aecef32. Report an issue: GitHub.

Appendix: source

Thrown at gix-attributes/src/search/outcome.rs:377

}

/// A version of `Match` without references.
#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
pub struct Match {
    /// The glob pattern itself, like `/target/*`.
    pub pattern: RefMapKey,
    /// The key=value pair of the attribute that matched at the pattern. There can be multiple matches per pattern.
    pub assignment: RefMapKey,
    /// Additional information about the kind of match.
    pub kind: MatchKind,
    /// Information about the location of the match.
    pub location: MatchLocation,
}

impl Match {
    fn to_outer<'a>(&self, out: &'a Outcome) -> crate::search::Match<'a> {
        crate::search::Match {
            pattern: out.patterns.resolve(self.pattern).expect("pattern still present"),
            assignment: out
                .assignments
                .resolve(self.assignment)
                .expect("assignment present")
                .as_ref(),
            kind: self.kind,
            location: self.location.to_outer(out),
        }
    }
}

/// A version of `MatchLocation` without references.
#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
pub struct MatchLocation {
    /// The path to the source from which the pattern was loaded, or `None` if it was specified by other means.
    pub source: Option<RefMapKey>,
    /// The line at which the pattern was found in its `source` file, or the occurrence in which it was provided.
    pub sequence_number: usize,

View on GitHub (pinned to e73179060b)