ipfs/kubo · error
too many levels of symbolic links: %s
Error message
too many levels of symbolic links: %s
What it means
Before the atomic write, resolveSymlink follows the output path's symlink chain manually (so a final dangling link still resolves). It follows at most maxSymlinkHops (8) links; if the chain is longer it refuses with this ELOOP-style error rather than looping forever. Nearly always this means a genuine symlink loop (a -> b -> a) or an absurdly deep chain.
Source
Thrown at core/commands/keystore.go:412
}
target, err := os.Readlink(path)
if err != nil {
return "", err
}
if !filepath.IsAbs(target) {
// A relative target starts at the directory the link lives in,
// with that directory's own links resolved. Joining it onto the
// path as typed would collapse ".." through them and name a file
// the kernel never points at.
dir, err := filepath.EvalSymlinks(filepath.Dir(path))
if err != nil {
return "", err
}
target = filepath.Join(dir, target)
}
path = target
}
return "", fmt.Errorf("too many levels of symbolic links: %s", path)
}
// writeExportedKeyInPlace writes to an existing character device or pipe. The
// target type is confirmed on the open descriptor, so a path swapped for a
// regular file after the stat cannot receive the key.
func writeExportedKeyInPlace(outPath string, writeKey func(io.Writer) error) (err error) {
// No O_TRUNC: devices and pipes ignore it, and a regular file that reached
// this path through a race must not be emptied.
file, err := os.OpenFile(outPath, os.O_WRONLY, 0)
if err != nil {
return err
}
defer func() { err = errors.Join(err, file.Close()) }()
info, err := file.Stat()
if err != nil {
return err
}View on GitHub (pinned to 329838acdf)
Solutions
- Inspect the chain: `ls -la` each link or `readlink -f <path>` (which will also report the loop)
- Delete and recreate the offending symlink to point at a real file/directory
- Do not point the -o target at a link that (transitively) points back to itself
- Note the tool never creates symlinks, so the loop was created externally and must be removed externally
Example fix
// before ln -s b a && ln -s a b && ipfs key export mykey -o a // after rm a b && ipfs key export mykey -o a.pem
Defensive patterns
Strategy: validation
Validate before calling
# reject symlink loops before export realpath -e "$out" >/dev/null 2>&1 || echo 'path does not resolve (possible loop)' # count hops p="$out"; for i in $(seq 1 9); do l=$(readlink "$p" || break); p="$l"; done; [ $i -lt 9 ] || echo 'too many symlink levels'
Prevention
- Run `readlink -f <out>` to verify the path resolves before exporting
- Clean up loops in /tmp and fixture directories immediately after tests
- Avoid chained symlinks for key storage paths; link directly to the real file
When it happens
Trigger: `ipfs key export -o <path>` where <path> contains a symlink cycle, e.g. `ln -s a b && ln -s b a` then exporting to `a`. Also theoretically chains deeper than 8 links.
Common situations: Accidental self-referential symlinks left by broken provisioning scripts or failed deploys; test fixtures that leaked loops into /tmp; symlinking the key output path to itself.
Related errors
- refusing to export key to %s: not a regular file, character
- creating temporary file for %s: %w
- flushing %s: %w
- writing %s: %w
- refusing to export key to %s: it changed type while being op
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/cafe8080112ea389.
Report an issue: GitHub.