Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 8 min read Last reviewed Jul 2026

Linux Error Guide: 'Too many levels of symbolic links' — break the symlink loop

Quick answer

Fix 'Too many levels of symbolic links' (ELOOP): find and break circular or self-referencing symlinks, spot bind-mount loops, and safely repair broken link chains on Linux.

  • #linux
  • #troubleshooting
  • #errors
  • #filesystem
Free toolkit

Stuck on this Linux Admins error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

Accessing a file or directory fails, and the tool reports that it followed too many symbolic links while resolving the path:

ls: cannot access '/opt/app/current': Too many levels of symbolic links

The raw errno behind it is ELOOP, and you will also see it phrased by the C library as:

bash: cd: /opt/app/current: Too many levels of symbolic links

Linux caps how many symlinks it will follow when resolving a single path (typically 40). When a symlink points — directly or through a chain — back to itself, the kernel hits that limit and returns ELOOP rather than looping forever. The error is protecting you from an infinite resolution loop; the fix is to find and break the loop.

Symptoms

  • ls, cd, cat, stat, or an application fails on one specific path with “Too many levels of symbolic links”.
  • The path involves a symlink (often a current / latest pointer in a deploy layout) that was just changed.
  • readlink -f <path> also errors instead of printing a resolved target.
  • ls -l on the link shows it pointing at itself or at another link that points back.
  • Only the looping path is affected; unrelated files on the same filesystem work fine.

Common Root Causes

  • A self-referencing symlinkln -s current current or a relative target that resolves to the link itself.
  • A circular chaina -> b, b -> c, c -> a, common after a scripted deploy re-points links in the wrong order.
  • A bad relative target — a symlink using . or a relative path that, from its own directory, points back at itself.
  • A botched atomic-deploy swap where the current symlink was repointed to a directory that itself contains a link back to current.
  • Bind mounts or overlay setups that create a directory that mounts onto a path inside itself, producing a loop during resolution.
  • A restored/rsync’d tree that copied symlinks with absolute targets that no longer match the new layout.

Diagnostic Workflow

Inspect the link itself without following it (ls/readlink on the link, not through it):

ls -l /opt/app/current             # shows "current -> current" or the next hop
readlink /opt/app/current          # one level: the literal target
readlink -f /opt/app/current       # full resolution (will error on a true loop)

Walk the chain hop by hop to locate where it closes:

namei -l /opt/app/current          # prints every component and each symlink target

namei output for a loop makes the cycle obvious:

f: /opt/app/current
 l current -> releases/active
 l active -> ../current

Find self-referencing or clearly broken links under a tree:

find /opt/app -maxdepth 2 -type l -exec sh -c 'readlink -f "$1" >/dev/null 2>&1 || echo "BROKEN/LOOP: $1 -> $(readlink "$1")"' _ {} \;

If you suspect a mount loop rather than a plain symlink, check the mount table:

findmnt -R /opt/app
mount | grep -i /opt/app

Example Root Cause Analysis

A deploy script used the standard current -> releases/<timestamp> pattern for atomic releases. After a release, the app started returning 500s and cd /srv/app/current failed with Too many levels of symbolic links. Inspecting the link:

ls -l /srv/app/current
lrwxrwxrwx 1 deploy deploy 7 Jul  6 02:14 current -> current

The deploy step meant to run ln -sfn releases/20260706021400 current from inside /srv/app, but a variable expanded empty, so the command became ln -sfn current current — the link now pointed at itself. Because ln -sfn replaces the existing link in place, the loop was created atomically with no error at deploy time. The fix was to re-point it correctly and force-replace the loop:

cd /srv/app
ln -sfn releases/20260706021400 current
readlink -f current
/srv/app/releases/20260706021400

The lasting fix was to make the deploy script fail fast when the release variable is empty (set -u plus a guard) so ln can never be handed a target equal to the link name.

Prevention Best Practices

  • In deploy scripts, use set -euo pipefail and validate that the symlink target variable is non-empty before calling ln.
  • Always create/replace pointer symlinks with ln -sfn <target> <link> and never let the target equal the link name.
  • Prefer absolute or clearly-scoped relative targets, and verify with readlink -f immediately after creating a link.
  • Add a post-deploy health check that runs readlink -f current (or test -d current) and rolls back if resolution fails.
  • Keep atomic-swap logic in one reviewed helper rather than inline ln calls scattered across scripts.
  • When copying trees with rsync/tar, decide deliberately between preserving symlinks (-l) and dereferencing them (-L) so targets stay valid in the new location.

Quick Command Reference

# Inspect the link without following it
ls -l <path>
readlink <path>          # one hop
readlink -f <path>       # full resolution (errors on a loop)

# Trace every component and target
namei -l <path>

# Find broken or looping symlinks under a tree
find <dir> -type l -exec sh -c \
  'readlink -f "$1" >/dev/null 2>&1 || echo "BAD: $1 -> $(readlink "$1")"' _ {} \;

# Rule out a mount loop
findmnt -R <path>

# Correctly (re)point a deploy symlink
ln -sfn <real-target> <link>

Conclusion

“Too many levels of symbolic links” (ELOOP) means the kernel gave up resolving a path because a symlink pointed back into its own chain — a safety limit, not a disk fault. Inspect the link itself with ls -l and readlink (never through it), then use namei -l to see exactly where the cycle closes, and re-point the offending link with ln -sfn. Most real-world cases come from deploy scripts that fed ln an empty or self-referential target, so the durable fix is a fail-fast script that validates its target and a post-deploy check that confirms readlink -f resolves before traffic is sent.

Free download · 368-page PDF

Fixed it? Get 500 Linux Admins & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.