kopia/kopia · error
cannot resolve
Error message
cannot resolve '%q'
What it means
resolveSymlink follows symlinks a bounded number of times; if the chain still has not resolved to a file after that many hops, it wraps errTooManySymlinks with "cannot resolve '%q'". This guards against symlink cycles and pathologically deep chains during ignore-rule processing.
Solutions
- Find and break the symlink cycle: `find -L . -type l` or `namei -l <path>` to locate the loop
- Remove or repoint the cyclic symlink
- Exclude the problematic directory from the scan via ignore rules
Example fix
// before ln -s . parent-link // creates infinite loop // after rm parent-link // remove the cyclic symlink or link to a bounded target
Defensive patterns
Strategy: validation
Validate before calling
if tgt, err := filepath.EvalSymlinks(path); err != nil { return fmt.Errorf("symlink chain unresolved for %s: %w", path, err) } Type guard
func hasSymlinkLoop(root string) error { return filepath.Walk(root, func(p string, fi os.FileInfo, err error) error { if err != nil { return err }; if fi.Mode()&os.ModeSymlink != 0 { _, e := filepath.EvalSymlinks(p); return e } ; return nil }) } Try / catch
if err != nil {
log.Warnf("symlink cycle at %s, excluding from scan", path)
return filepath.SkipDir
} Prevention
- Never create symlinks that point to ancestor directories (ln -s . ...)
- Run `find -L . -type l` in CI to detect loops before snapshotting
- Keep symlink chain depth shallow (1-2 hops)
When it happens
Trigger: A symlink loop (a -> b -> a) or a chain longer than the resolver's iteration limit is encountered while buildContext walks the tree.
Common situations: Accidental self-referencing links (ln -s . loop), circular links created by misconfigured deployment tooling, or extremely deep symlink chains in package-manager directories.
Related errors
- too many levels of symbolic links
- error reading link
- lstat error at symlink path
- resolveSymlink
- does not eventually link to a file
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/1d3a0dc44d1cf73a.
Report an issue: GitHub.
Appendix: source
Thrown at fs/ignorefs/ignorefs.go:299
for range maxSymlinkFollow {
target, err := entry.Resolve(ctx)
if err != nil {
link, _ := entry.Readlink(ctx)
return nil, errors.Wrapf(err, "when resolving symlink %s of type %T, which points to %s", entry.Name(), entry, link)
}
switch t := target.(type) {
case fs.File:
return t, nil
case fs.Symlink:
entry = t
continue
default:
return nil, errors.Wrapf(errSymlinkNotAFile, "%s does not eventually link to a file", entry.Name())
}
}
return nil, errors.Wrapf(errTooManySymlinks, "cannot resolve '%q'", entry.Name())
}
func (d *ignoreDirectory) buildContext(ctx context.Context) (*ignoreContext, error) {
effectiveDotIgnoreFiles := d.parentContext.dotIgnoreFiles
pol := d.policyTree.DefinedPolicy()
if pol != nil {
effectiveDotIgnoreFiles = pol.FilesPolicy.DotIgnoreFiles
}
var dotIgnoreFiles []fs.File
for _, dotfile := range effectiveDotIgnoreFiles {
if e, err := d.Directory.Child(ctx, dotfile); err == nil {
switch entry := e.(type) {
case fs.File:
dotIgnoreFiles = append(dotIgnoreFiles, entry)
View on GitHub (pinned to 82495e54b5)