nektos/act · error
opening seccomp profile (%s) failed: %w
Error message
opening seccomp profile (%s) failed: %w
What it means
For --security-opt seccomp=<file> where <file> is not the built-in 'default'/'unconfined' names, act reads the file locally and sends its (compacted JSON) contents to the daemon. This error means os.ReadFile on the profile path failed — the file does not exist at that path, or is not readable by the act process. The path is resolved on the host running act, not inside the container.
Source
Thrown at pkg/container/docker_cli.go:955
for key, opt := range securityOpts {
k, v, ok := strings.Cut(opt, "=")
if !ok && k != "no-new-privileges" {
k, v, ok = strings.Cut(opt, ":")
}
if (!ok || v == "") && k != "no-new-privileges" {
// "no-new-privileges" is the only option that does not require a value.
return securityOpts, fmt.Errorf("invalid --security-opt: %q", opt)
}
if k == "seccomp" {
switch v {
case seccompProfileDefault, seccompProfileUnconfined:
// known special names for built-in profiles, nothing to do.
default:
// value may be a filename, in which case we send the profile's
// content if it's valid JSON.
f, err := os.ReadFile(v)
if err != nil {
return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %w", v, err)
}
var b bytes.Buffer
if err := json.Compact(&b, f); err != nil {
return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %w", v, err)
}
securityOpts[key] = "seccomp=" + b.String()
}
}
}
return securityOpts, nil
}
// parseSystemPaths checks if `systempaths=unconfined` security option is set,
// and returns the `MaskedPaths` and `ReadonlyPaths` accordingly. An updated
// list of security options is returned with this option removed, because the
// `unconfined` option is handled client-side, and should not be sent to the
// daemon.View on GitHub (pinned to 4f41128141)
Solutions
- Point at an absolute host path that exists and is readable: --security-opt seccomp=$PWD/seccomp.json
- Verify with ls on the host running act, not inside the image
- If you just want Docker's default behavior, use seccomp=default or drop the flag
- Fix file permissions if unreadable (chmod a+r)
Example fix
# before (path relative to container/ci) options: --security-opt seccomp=etc/seccomp/profile.json # after (absolute host path) options: --security-opt seccomp=/home/me/project/etc/seccomp/profile.json
Defensive patterns
Strategy: validation
Validate before calling
// resolve and check the profile on the HOST before passing the option
if k == "seccomp" && !isBuiltinSeccompName(v) {
if _, err := os.Stat(v); err != nil {
return fmt.Errorf("seccomp profile %s not readable on host: %w", v, err)
}
if data, err := os.ReadFile(v); err == nil {
if !json.Valid(data) { return fmt.Errorf("profile %s is not valid JSON", v) }
}
} Try / catch
f, err := os.ReadFile(v)
if err != nil {
return fmt.Errorf("seccomp profile %q unreadable (must exist on the act host): %w", v, err)
} Prevention
- Use absolute host paths for seccomp profiles
- Remember act resolves paths where act runs, not inside the container image
- Use seccomp=unconfined or default when no custom profile file is available
When it happens
Trigger: Passing --security-opt seccomp=/path/to/profile.json where the path is missing, relative to a different working directory, permission-denied, or intended to live inside the container image instead of on the host.
Common situations: Workflow authored for real GitHub Actions runners where the profile ships in the repo but act runs on a different machine/cwd; relative paths breaking when act's cwd differs; profiles referenced by container-absolute paths like /etc/docker/seccomp.json that don't exist on the host.
Related errors
- invalid --security-opt: %q
- --health-start-interval cannot be negative
- %s is not a valid mac address
- invalid value: %d. Valid memory swappiness range is 0-100
- invalid range format for --expose: %w
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/68dcb133e318102c.
Report an issue: GitHub.