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

  1. Inspect the chain: `ls -la` each link or `readlink -f <path>` (which will also report the loop)
  2. Delete and recreate the offending symlink to point at a real file/directory
  3. Do not point the -o target at a link that (transitively) points back to itself
  4. 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

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


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/cafe8080112ea389. Report an issue: GitHub.