dagger/dagger · error

too many links

Error message

too many links

What it means

errTooManyLinks is a sentinel error thrown by the contenthash path resolver (rootPath and the checksum walker) when resolving a path encounters more than maxSymlinkLimit (255) symbolic links. The limit bounds symlink traversal so deeply or cyclically linked trees cannot cause infinite recursion or resource exhaustion. If you see it, the path being checksummed or resolved contains a symlink chain (typically a cycle like a -> b -> a, or a chain) longer than 255 hops.

Source

Thrown at engine/contenthash/path.go:18

// This code mostly comes from <https://github.com/cyphar/filepath-securejoin>.

// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package contenthash

import (
	"os"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"
)

var errTooManyLinks = errors.New("too many links")

const maxSymlinkLimit = 255

type onSymlinkFunc func(string, string) error

// rootPath joins a path with a root, evaluating and bounding any symlink to
// the root directory. This is a slightly modified version of SecureJoin from
// github.com/cyphar/filepath-securejoin, with a callback which we call after
// each symlink resolution.
func rootPath(root, unsafePath string, followTrailing bool, cb onSymlinkFunc) (string, error) {
	if unsafePath == "" {
		return root, nil
	}

	unsafePath = filepath.FromSlash(unsafePath)
	var (
		currentPath string
		linksWalked int

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Inspect the path named in your command with `ls -la` and `find -L <path> -maxdepth 300` to locate the symlink cycle or over-long chain.
  2. Remove or rewrite the offending symlink so it no longer points to an ancestor of itself (break the loop).
  3. Rebuild the affected directory without the loop (re-clone, restore from a clean cache, or regenerate the tree).
  4. If a genuinely deep (>255) symlink chain is required, restructure it — flatten the chain or copy the target instead of chaining links, since the 255 limit is fixed.

Example fix

// inside the container, a link pointing back at its ancestor
// before
ln -s / /work/root-link   // /work/root-link/... can loop forever
// after
rm /work/root-link
ln -s /shared/data /work/data-link
Defensive patterns

Strategy: validation

Validate before calling

import { execSync } from 'node:child_process'
function hasSymlinkLoop(root) {
  try {
    // find -L reports 'too many levels of symbolic links' on cycles
    execSync(`find -L ${JSON.stringify(root)} -maxdepth 300 -type f -quit`, { stdio: 'ignore' })
    return false
  } catch (e) {
    return /Too many levels of symbolic links/i.test(e.stderr?.toString() ?? '')
  }
}
if (hasSymlinkLoop('/work')) throw new Error('symlink loop under /work; fix before checksum')

Prevention

When it happens

Trigger: Calling rootPath (used by checksum, stat, include/exclude walking) on a path whose symlink chain exceeds 255 links — most commonly a symlink loop (a/b/c pointing back to an ancestor) created inside a container or mounted directory; also legitimately deep symlink chains (256+) in generated trees.

Common situations: A Dockerfile or script accidentally creates a self-referential symlink (e.g. ln -s . loop); a symlinked cache dir pointing back into itself after a restore; CI tooling symlink-spam in node_modules or vendored dirs; bind-mounting a directory into itself.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/a01337937f4f2c1a. Report an issue: GitHub.