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

  1. Point at an absolute host path that exists and is readable: --security-opt seccomp=$PWD/seccomp.json
  2. Verify with ls on the host running act, not inside the image
  3. If you just want Docker's default behavior, use seccomp=default or drop the flag
  4. 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

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


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