jdx/mise · error

static credential globs

Error message

static credential globs

What it means

glob_set builds a GlobSet from patterns that were already filtered with Glob::new(... if let Ok), so builder.build() is expected to be infallible and is unwrapped with expect("static credential globs"). A panic means GlobSetBuilder rejected a glob that Glob::new accepted, an upstream-inconsistency scenario.

Solutions

  1. Update the glob/globset crates to compatible versions
  2. Verify the credential glob patterns compile with Glob::new in isolation if debugging
  3. Replace expect with a graceful error if patterns ever become user-validated rather than compile-time static
Defensive patterns

Strategy: fallback

Validate before calling

// verify patterns compile before use
for p in patterns {
    Glob::new(p).expect("invalid credential glob pattern");
}

Try / catch

let globset = builder.build()
    .map_err(|e| anyhow!("failed to build credential globs: {e}"))?;

Prevention

When it happens

Trigger: Practically unreachable for users; fires only if GlobSetBuilder::build fails on globs already validated by Glob::new, e.g. after a glob crate version change alters build() validation.

Common situations: Maintainers touching the credential glob builder after a glob/globset dependency update.

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 jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/e07fdf1ce14354da. Report an issue: GitHub.

Appendix: source

Thrown at src/system/history/tracked.rs:638

/// Credential stores mise itself knows by name; they mean something only
/// under the global configuration directory.
fn credential_names() -> GlobSet {
    glob_set(CREDENTIAL_NAMES)
}

/// Key material by name pattern, private wherever it is captured.
fn credential_globs() -> GlobSet {
    glob_set(CREDENTIAL_GLOBS)
}

fn glob_set(patterns: &[&str]) -> GlobSet {
    let mut builder = GlobSetBuilder::new();
    for pattern in patterns {
        if let Ok(glob) = Glob::new(pattern) {
            builder.add(glob);
        }
    }
    builder.build().expect("static credential globs")
}

/// The `[history] exclude` globs, applied in order with the last match
/// deciding: a `!glob` after a broader glob re-includes what it matches.
#[derive(Debug, Default)]
pub(crate) struct ExcludeSet {
    patterns: Vec<(globset::GlobMatcher, bool)>,
}

impl ExcludeSet {
    pub(crate) fn new(globs: &[String]) -> Result<Self> {
        let mut patterns = vec![];
        for glob in globs {
            let (pattern, negated) = match glob.strip_prefix('!') {
                Some(rest) => (rest, true),
                None => (glob.as_str(), false),
            };
            let expanded = file::replace_path(Path::new(pattern));

View on GitHub (pinned to 533346cc37)