docker/cli · error · invalidParameterErr

invalid detach keys ( )

Error message

invalid detach keys (%s): %w

What it means

Returned by validateDetachKeys (hijack.go:38) when term.ToBytes cannot parse the --detach-keys value. Detach keys are converted to a byte sequence; an unparseable specifier (e.g. an unknown key name) produces this error, wrapped by invalidParameter so it is treated as a client-side usage error.

Solutions

  1. Use the documented format, e.g. --detach-keys=ctrl-p,ctrl-x or a single key like --detach-keys=q.
  2. Check ~/.docker/config.json detachKeys if set globally.
  3. Remove the flag to use the default (ctrl-p ctrl-q).

Example fix

# before
docker attach --detach-keys=ctrl+p&ctrl+x box

# after
docker attach --detach-keys=ctrl-p,ctrl-x box
Defensive patterns

Strategy: validation

Validate before calling

// Validate detach-keys syntax the same way the CLI does:
import "github.com/moby/term"
if _, err := term.ToBytes(detachKeys); err != nil {
    return fmt.Errorf("bad --detach-keys %q: %w", detachKeys, err)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "invalid detach keys") {
    // fall back to the default ctrl-p,ctrl-q by dropping the flag
}

Prevention

When it happens

Trigger: Passing `--detach-keys` with a value term.ToBytes cannot understand: an unrecognized token, malformed escape, or a sequence with trailing garbage. Common in `docker attach`, `docker exec`, `docker run -it`.

Common situations: Typo in a key name (e.g. ctrl-p&ctrl-x mistyped), copy-paste of an unsupported sequence, or a config-file detachKeys value from an older/incompatible Docker version.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/6fbc57c4b87bc528. Report an issue: GitHub.

Appendix: source

Thrown at cli/command/container/hijack.go:38

// readCloserWrapper wraps an io.Reader, and implements an io.ReadCloser
// It calls the given callback function when closed.
type readCloserWrapper struct {
	io.Reader
	closer func() error
}

// Close calls back the passed closer function
func (r *readCloserWrapper) Close() error {
	return r.closer()
}

func validateDetachKeys(keys string) error {
	if keys == "" {
		return nil
	}
	if _, err := term.ToBytes(keys); err != nil {
		return invalidParameter(fmt.Errorf("invalid detach keys (%s): %w", keys, err))
	}
	return nil
}

// A hijackedIOStreamer handles copying input to and output from streams to the
// connection.
type hijackedIOStreamer struct {
	streams      command.Streams
	inputStream  io.ReadCloser
	outputStream io.Writer
	errorStream  io.Writer

	resp client.HijackedResponse

	tty        bool
	detachKeys string
}

View on GitHub (pinned to 4f84911bfe)