air-verse/air · error

unexpected error while dereferencing %v: %w

Error message

unexpected error while dereferencing %v: %w

What it means

After absolutizing, expandPath calls filepath.EvalSymlinks to dereference the path. EvalSymlinks errors that are NOT fs.ErrNotExist (e.g. ELOOP symlink cycles, EACCES) are unexpected and returned wrapped in this error.

Source

Thrown at runner/util.go:290

	if strings.HasPrefix(path, "~/") {
		home := os.Getenv("HOME")
		expanded = filepath.Join(home, path[1:])
	}

	expanded, err := filepath.Abs(expanded)
	if err != nil {
		return "", fmt.Errorf("error getting absolute path to %v: %w", expanded, err)
	}

	// filepath.EvalSymlinks only works on real files
	dereferenced, err := filepath.EvalSymlinks(expanded)
	if err == nil {
		return dereferenced, nil
	}

	if !errors.Is(err, fs.ErrNotExist) {
		return "", fmt.Errorf("unexpected error while dereferencing %v: %w", expanded, err)
	}

	return expanded, nil
}

func isDir(path string) bool {
	i, err := os.Stat(path)
	if err != nil {
		return false
	}
	return i.IsDir()
}

func validEvent(ev fsnotify.Event) bool {
	return ev.Op&fsnotify.Create == fsnotify.Create ||
		ev.Op&fsnotify.Write == fsnotify.Write ||
		ev.Op&fsnotify.Remove == fsnotify.Remove ||
		ev.Op&fsnotify.Rename == fsnotify.Rename

View on GitHub (pinned to 71ea1dee05)

Solutions

  1. Inspect the wrapped %w error (syscall.ELOOP → symlink cycle; EACCES → permissions) and fix accordingly
  2. Remove or repair circular symlinks in the path (`find -L . -type l` can help locate loops)
  3. Check permissions on every path component (`ls -la` each ancestor)
  4. Bypass the symlink by configuring air with the real, non-symlinked path

Example fix

// before: project is a symlink loop
ln -s proj proj-link && ln -s proj-link proj
// after: point config at the real directory
root = "/home/user/proj"  // actual directory, not a loop
Defensive patterns

Strategy: try-catch

Validate before calling

// check the path resolves cleanly before handing it to air
resolved, err := filepath.EvalSymlinks(projectDir)
if err != nil {
	return fmt.Errorf("project path not resolvable: %w", err)
}
_ = resolved

Try / catch

_, err := filepath.EvalSymlinks(dir)
if err != nil {
	switch {
	case errors.Is(err, syscall.ELOOP):
		// fix symlink cycle
	case errors.Is(err, fs.ErrPermission):
		// fix permissions
	}
	return err
}

Prevention

When it happens

Trigger: expandPath receives a path whose symlink resolution fails with a non-ErrNotExist error: a symlink loop, permission denied on a parent directory, or too many levels of symbolic links during config preprocessing.

Common situations: Circular symlinks in the project or home directory; a symlinked project path where a component is unreadable after a permissions change; NFS/mount issues making components inaccessible.

Related errors


AI-assisted analysis of air-verse/air@71ea1dee05 (2026-08-31). Data as JSON: /api/errors/a66df70663e6c25e. Report an issue: GitHub.