nektos/act · error
opening seccomp profile
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.
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
- compacting json for seccomp profile
- failed to handle credentials
- failed to handle service
- failed to remove image
AI-assisted analysis of nektos/act@4f41128141 (2026-08-15).
Data as JSON: /api/errors/68dcb133e318102c.
Report an issue: GitHub.
Appendix: 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)