docker/cli · error
invalid --security-opt
Error message
invalid --security-opt: %q
What it means
Thrown by parseSecurityOpts when a --security-opt value cannot be split into key=value (or key:value) and is not the single valueless option 'no-new-privileges'. The CLI requires every security option except 'no-new-privileges' to carry a value. This is a client-side validation performed before the request reaches the daemon.
Solutions
- Add a value via '=' or ':' separator: --security-opt seccomp=unconfined or --security-opt seccomp:unconfined
- Use the exact bare keyword only for the valueless option: --security-opt no-new-privileges
- For apparmor, use --security-opt apparmor=<profile-name>
Example fix
# before docker run --security-opt seccomp # after docker run --security-opt seccomp=unconfined
Defensive patterns
Strategy: validation
Validate before calling
// Validate --security-opt tokens before passing to the run/create call.
func validSecurityOpt(opt string) bool {
if opt == "no-new-privileges" {
return true
}
if _, _, ok := strings.Cut(opt, "="); ok {
return true
}
if _, _, ok := strings.Cut(opt, ":"); ok {
return true
}
return false
}
for _, o := range securityOpts {
if !validSecurityOpt(o) {
return fmt.Errorf("refusing bad --security-opt %q", o)
}
} Prevention
- Allowlist known security option keys (seccomp, apparmor, systempaths, no-new-privileges) in your wrapper
- Write a preflight linter that rejects valueless tokens other than no-new-privileges
When it happens
Trigger: Calling `docker run --security-opt <bare-word>` where the token contains neither '=' nor ':', e.g. `--security-opt seccomp` or `--security-opt apparmor`. The function first tries Cut on '=', then on ':', and only 'no-new-privileges' is exempt from requiring a value.
Common situations: Forgetting the value when enabling seccomp/apparmor profiles; typoing a flag like `--security-opt no-new-privileges:true` (the colon-split makes k='no-new-privileges', v='true' which is accepted, but bare `--security-opt no-new` fails); copy-pasting partial examples from docs.
Related errors
- opening seccomp profile
- compacting json for seccomp profile
- invalid device specification
- every ip-range or gateway must have a corresponding subnet
- multiple overlapping subnet configuration is not supported
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/960337b204b367c7.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command/container/opts.go:937
func parseLoggingOpts(loggingDriver string, loggingOpts []string) (map[string]string, error) {
loggingOptsMap := opts.ConvertKVStringsToMap(loggingOpts)
if loggingDriver == "none" && len(loggingOpts) > 0 {
return map[string]string{}, fmt.Errorf("invalid logging opts for driver %s", loggingDriver)
}
return loggingOptsMap, nil
}
// takes a local seccomp daemon, reads the file contents for sending to the daemon
func parseSecurityOpts(securityOpts []string) ([]string, error) {
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()
}View on GitHub (pinned to 4f84911bfe)