gastownhall/beads · error

symbolic ref chain for %q exceeds 16 links

Error message

symbolic ref chain for %q exceeds 16 links

What it means

The symbolic ref walker caps chain length at 16 links; a valid ref chain in any real repository terminates in far fewer hops. If the loop guard (cycle detection) has not fired after 16 hops, the chain is treated as pathological. This is a safety valve against unbounded git invocations.

Source

Thrown at cmd/bd/worktree_cmd.go:1928

		}
		seen[current] = struct{}{}

		output, err := git.output(ctx, executionRoot, "symbolic-ref", "--quiet", current)
		if err == nil {
			next := strings.TrimSpace(string(output))
			if !strings.HasPrefix(next, "refs/") {
				return "", fmt.Errorf("symbolic ref %q resolves outside refs/: %q", current, next)
			}
			current = next
			continue
		}
		var exitError *exec.ExitError
		if errors.As(err, &exitError) && exitError.ExitCode() == 1 {
			return current, nil
		}
		return "", fmt.Errorf("failed to inspect symbolic ref %q: %w", current, err)
	}
	return "", fmt.Errorf("symbolic ref chain for %q exceeds 16 links", ref)
}

func resolveWorktreeCommitOID(
	ctx context.Context,
	git *worktreeRemovalGit,
	executionRoot string,
	refOrOID string,
) (string, error) {
	output, err := git.output(
		ctx,
		executionRoot,
		"rev-parse",
		"--verify",
		"--quiet",
		"--end-of-options",
		refOrOID+"^{commit}",
	)
	if err != nil {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Find the chain endpoint with repeated `git symbolic-ref` calls and flatten it: repoint the first ref directly at the final branch
  2. Replace the whole chain with a single hard ref using `git update-ref`
  3. Delete the redundant intermediate refs with `git update-ref -d`

Example fix

// before: long chain refs/a -> refs/b -> ... -> refs/heads/main
$ git symbolic-ref refs/a refs/heads/main
// after: single hop
$ git symbolic-ref refs/a refs/heads/main
Defensive patterns

Strategy: validation

Validate before calling

// bound-check the symbolic chain length before invoking bd
func chainDepth(ref string, sym func(string) (string, bool)) int {
	for depth := 0; depth < 16; depth++ {
		next, ok := sym(ref)
		if !ok { return depth }
		ref = next
	}
	return -1 // exceeds safe depth
}

Try / catch

_, err := bd.WorktreeRemove(name)
if err != nil && strings.Contains(err.Error(), "exceeds 16 links") {
	// flatten the ref chain manually, then retry
	return flattenRefsAndRetry(name)
}

Prevention

When it happens

Trigger: A symbolic ref chain of 17+ distinct links — possible only with adversarially or programmatically created refs, e.g. refs/a -> refs/b -> ... -> refs/q — encountered while resolving the comparison ref for worktree removal.

Common situations: Automated tooling that chains symbolic refs (each ref pointing to the next); migration scripts that rebuilt refs incorrectly; intentionally crafted repositories.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d316ce1bd5faa612. Report an issue: GitHub.