docker/cli · error
compacting json for seccomp profile
Error message
compacting json for seccomp profile (%s) failed: %w
What it means
Thrown by parseSecurityOpts after successfully reading a seccomp profile file when json.Compact fails, meaning the file content is not valid JSON. The CLI compacts the JSON before embedding it inline in the security option sent to the daemon.
Solutions
- Validate the JSON with a parser: jq . /path/to/profile.json
- Start from the default profile (docker default seccomp profile) and modify incrementally
- Remove JSON comments (not valid in strict JSON) and trailing commas
- Re-save the file as UTF-8 without BOM
Example fix
# before (profile.json contains a comment)
{
// seccomp rules
"defaultAction": "SCMP_ACT_ERRNO"
}
# after
{
"defaultAction": "SCMP_ACT_ERRNO"
} Defensive patterns
Strategy: validation
Validate before calling
// Validate JSON content of a seccomp profile before passing it to docker.
func validSeccompJSON(path string) error {
b, err := os.ReadFile(path)
if err != nil {
return err
}
var v any
return json.Unmarshal(b, &v)
}
if err := validSeccompJSON(profile); err != nil {
return fmt.Errorf("seccomp profile not valid JSON: %w", err)
} Prevention
- Lint profiles with jq as a pre-commit hook
- Generate profiles programmatically with encoding/json rather than hand-editing
When it happens
Trigger: Calling `docker run --security-opt seccomp=/path/to/profile.json` where the file is readable but contains syntax errors (trailing commas, comments, unquoted keys, truncated content, or non-JSON like YAML/INI).
Common situations: Hand-editing the profile and leaving a syntax error; saving the file in a different format; CRLF/encoding issues; copying a profile snippet that was incomplete; profile generated by a tool that emits YAML.
Related errors
- cluster options are incompatible with type image
- invalid bind source, source cannot be empty
- volume options are incompatible with type bind
- image options are incompatible with type bind
- tmpfs options are incompatible with type bind
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/3d2e993b99d510f5.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/container/opts.go:952
}
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.
func parseSystemPaths(securityOpts []string) (filtered, maskedPaths, readonlyPaths []string) {
filtered = securityOpts[:0]
for _, opt := range securityOpts {
if opt == "systempaths=unconfined" {View on GitHub (pinned to 4f84911bfe)