docker/cli · error
unexpected hook response type: ${Type}
Error message
unexpected hook response type: ${Type} What it means
When the CLI invokes a plugin's hook subcommand, it unmarshals the JSON output into hooks.Response and then asserts (cli-plugins/manager/hooks.go:95) that Response.Type equals hooks.NextSteps (the only currently-defined ResponseType, value 0). If a plugin returns any other Type value, the manager returns errors.New("unexpected hook response type: "+<int>) at line 96. The manager catches this per-plugin and logs it at Debug level (it skips misbehaving plugins rather than halting the CLI).
Source
Thrown at cli-plugins/manager/hooks.go:96
}
resp, err := p.RunHook(ctx, hooks.Request{
RootCmd: match,
Flags: flags,
CommandError: cmdErrorMessage,
})
if err != nil {
return nil, false, err
}
var message hooks.Response
if err := json.Unmarshal(resp, &message); err != nil {
return nil, false, fmt.Errorf("failed to unmarshal hook response (%q): %w", string(resp), err)
}
// currently the only hook type
if message.Type != hooks.NextSteps {
return nil, false, errors.New("unexpected hook response type: " + strconv.Itoa(int(message.Type)))
}
messages, err = hooks.ParseTemplate(message.Template, subCmd)
if err != nil {
return nil, false, err
}
return messages, true, nil
}
for pluginName, pluginCfg := range pluginsCfg {
messages, ok, err := tryInvokeHook(pluginName, pluginCfg)
if err != nil {
// skip misbehaving plugins, but don't halt execution
logrus.WithFields(logrus.Fields{
"error": err,
"plugin": pluginName,
}).Debug("Plugin hook invocation failed")View on GitHub (pinned to 4f84911bfe)
Solutions
- In the plugin's hook subcommand, always emit a JSON object with Type set to 0 (hooks.NextSteps): {"Type": 0, "Template": "..."}.
- Use the hooks.Response struct from github.com/docker/cli/cli-plugins/hooks so the wire format is correct.
- Verify the plugin binary is up to date and not a stale build using an older experimental response format.
- If you do not need hooks, remove the plugin's entry from the "hooks"/"error-hooks" config keys to prevent invocation.
Example fix
// before — plugin emits a custom type
resp := struct{ Type int; Template string }{Type: 2, Template: "done"}
// after — use the contract type
resp := hooks.Response{Type: hooks.NextSteps, Template: "done"} Defensive patterns
Strategy: validation
Validate before calling
// Plugin side: always construct the response from the contract type so Type is valid.
resp := hooks.Response{Type: hooks.NextSteps, Template: msg}
out, _ := json.Marshal(resp)
fmt.Println(string(out)) Type guard
// Host side: the manager already narrows by checking message.Type == hooks.NextSteps.
// Plugin authors can self-validate before printing:
func isValidResponse(r hooks.Response) bool { return r.Type == hooks.NextSteps } Try / catch
// The manager catches this per-plugin (cli-plugins/manager/hooks.go:108-116) and logs at Debug, // continuing with other plugins. Keep that non-fatal handling; do not rethrow.
Prevention
- Never invent custom ResponseType values; only hooks.NextSteps (0) is accepted.
- Build responses with the hooks.Response struct from the SDK, not an anonymous struct.
- Add an integration test that runs your plugin's hook subcommand and asserts Type == 0.
- Forward-compatibility: emit only known fields; ignore unknown request fields gracefully.
When it happens
Trigger: A CLI plugin hook subcommand prints a JSON object whose "Type" field is a non-zero integer (e.g. {"Type": 1, "Template": "..."}), or emits malformed/legacy output that unmarshals to a non-NextSteps Type. Most commonly happens when a plugin author invents a custom response type.
Common situations: Third-party plugins predating the hooks contract, plugins that treat Type as a string and produce a zero-value/integer mismatch, or plugins copied from a different hook protocol.
Related errors
- failed to unmarshal hook response (%q): %w
- failed to parse hook template
- plugin SchemaVersion version cannot be empty
- hook template contains too many messages (%d): maximum is %d
- failed to parse hook template: flagValue: no flags found
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/39bdeefcbae46dab.
Report an issue: GitHub.