nektos/act · error

symlink tries to access file '%s' outside of '%s'

Error message

symlink tries to access file '%s' outside of '%s'

What it means

Security guard for tar-based action reading: symlinkJoin resolves a symlink found inside an action archive and rejects it when the resolved destination escapes the parent directory (the action's root). The error names the offending destination and the allowed parent, with quotes doubled to keep the message injection-safe.

Source

Thrown at pkg/runner/step.go:374

		}
		foldKeys[strings.ToLower(foldKey)] = s
		return s
	}
	for _, m := range maps {
		for k, v := range m {
			target[toKey(k)] = v
		}
	}
}

func symlinkJoin(filename, sym, parent string) (string, error) {
	dir := path.Dir(filename)
	dest := path.Join(dir, sym)
	prefix := path.Clean(parent) + "/"
	if strings.HasPrefix(dest, prefix) || prefix == "./" {
		return dest, nil
	}
	return "", fmt.Errorf("symlink tries to access file '%s' outside of '%s'", strings.ReplaceAll(dest, "'", "''"), strings.ReplaceAll(parent, "'", "''"))
}

View on GitHub (pinned to 4f41128141)

Solutions

  1. If you own the action, replace the escaping symlink with a copy of the file or a relative symlink that stays inside the action directory.
  2. If it's a third-party action, audit its repository for unexpected symlinks before trusting it — this error may indicate a supply-chain attempt.
  3. Clear act's action cache (`~/.cache/act` or the configured cache dir) in case a corrupted archive was cached.
  4. Pin the action to a known-good full commit SHA of a version without the offending symlink.

Example fix

# inside the action repo, before (symlink escapes root):
#   .github/actions/tool/run -> ../../../shared/run.sh
# after (keep it inside):
#   .github/actions/tool/run -> ./run.sh  (copy shared/run.sh into the action dir)
Defensive patterns

Strategy: type-guard

Validate before calling

import ("path"; "strings")
func safeSymlink(filename, linkname, parent string) bool {
  dest := path.Join(path.Dir(filename), linkname)
  return strings.HasPrefix(dest, path.Clean(parent)+"/")
}

Type guard

func isSymlinkInBounds(filename, linkname, parent string) bool {
  dest := path.Join(path.Dir(filename), linkname)
  return strings.HasPrefix(dest, path.Clean(parent)+"/")
}

Prevention

When it happens

Trigger: An action's tar archive (local or cached remote action) contains a symlink whose target, joined with the current path, lands outside the action root — e.g. Linkname like '../../../../etc/passwd' or an absolute-ish traversal via path.Join semantics.

Common situations: Malicious or corrupted third-party action tarball attempting path traversal; an action legitimately symlinking to repo-root files in a way that resolves above the extracted directory (works on GitHub's full checkout but violates act's sandbox); cache poisoning supplying a tampered archive.

Related errors


AI-assisted analysis of nektos/act@4f41128141 (2026-08-15). Data as JSON: /api/errors/0b90db18ee2ac175. Report an issue: GitHub.