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
- 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.
- If it's a third-party action, audit its repository for unexpected symlinks before trusting it — this error may indicate a supply-chain attempt.
- Clear act's action cache (`~/.cache/act` or the configured cache dir) in case a corrupted archive was cached.
- 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
- Audit third-party actions for symlinks escaping their directory before use.
- Pin actions to full SHAs from trusted maintainers.
- Keep action self-contained: no symlinks above the action root.
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
- max depth %d of symlinks exceeded while reading %s
- max depth %d of symlinks exceeded while reading %s
- invalid --security-opt: %q
- opening seccomp profile (%s) failed: %w
- unable to readlink '%s': %w
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/0b90db18ee2ac175.
Report an issue: GitHub.