GitoxideLabs/gitoxide · warning

no symlink related errors

Error message

no symlink related errors

What it means

A Rust `expect()` panic in the loose-refs iterator's `next()` (gix-ref/store/file/loose/iter.rs). When `walkdir` (or similar) reports a traversal error, the code converts it to an `io::Error` with `expect("no symlink related errors")`, reflecting the iterator's assumption that symlink-following loops cannot occur because the traversal does not follow symlinks. If the walker is configured (or a platform reports) a symlink-related error, this assumption breaks and the code panics instead of returning an error.

Solutions

  1. Remove or replace symlinks inside `.git/refs` with real directories or files before iterating loose refs.
  2. Do not enable `follow_links` on the walker used for loose-ref iteration.
  3. Check for and repair symlink loops (`find -type l` under `.git/refs`) and fix permissions on the refs tree.
  4. If plain repositories trigger it, report upstream with the fs layout — internal invariant violation.

Example fix

// before
// refs/heads/master is a symlink loop -> walker error -> panic in next()
// after
$ find .git/refs -type l -delete   # or replace symlinks with real files
$ git fsck                         # verify repo health before iterating
Defensive patterns

Strategy: fallback

Validate before calling

// detect symlinks in refs tree before iterating
for entry in walkdir::WalkDir::new(".git/refs").follow_links(false) {
    if entry.as_ref().ok()?.file_type().is_symlink() { return Err("symlink in refs"); }
}

Try / catch

// pre-scan and refuse to iterate a refs tree containing symlinks
match contains_symlinks(".git/refs") {
    true => return Err(anyhow!("refs tree contains symlinks; not supported")),
    false => iterate_loose_refs(),
}

Prevention

When it happens

Trigger: Traversing loose refs where the directory walker encounters a symlink-related error condition (symlink loop detection, permission errors surfaced through symlink handling), i.e. when symlinked directories exist under `refs/` and the iterator's no-symlink-following assumption is violated by configuration or platform behavior.

Common situations: Repositories whose `refs/` tree contains symbolic links (manually created, or some sync/backup tools replacing directories with symlinks); custom walker configurations enabling follow_links; unusual filesystems (network mounts) reporting symlink errors.

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

Appendix: source

Thrown at gix-ref/src/store/file/loose/iter.rs:82

                    };
                    if let Some(prefix) = &self.prefix
                        && !full_name.starts_with(prefix)
                    {
                        continue;
                    }
                    if let Some(suffix) = &self.suffix
                        && !full_name.ends_with(suffix)
                    {
                        continue;
                    }
                    if gix_validate::reference::name_partial(full_name.as_bstr()).is_ok() {
                        let name = FullName(full_name);
                        return Some(Ok((full_path, name)));
                    } else {
                        continue;
                    }
                }
                Err(err) => return Some(Err(err.into_io_error().expect("no symlink related errors"))),
            }
        }
        None
    }
}

impl file::Store {
    /// Return an iterator over all loose references, notably not including any packed ones, in lexical order.
    /// Each of the references may fail to parse and the iterator will not stop if parsing fails, allowing the caller
    /// to see all files that look like references whether valid or not.
    ///
    /// Reference files that do not constitute valid names will be silently ignored.
    pub fn loose_iter(&self) -> std::io::Result<LooseThenPacked<'_, '_>> {
        self.iter_packed(None)
    }

    /// Return an iterator over all loose references that start with the given `prefix`.
    ///

View on GitHub (pinned to e73179060b)