docker/cli · warning

failed to unmarshal hook response

Error message

failed to unmarshal hook response (%q): %w

What it means

Thrown by the hook manager when json.Unmarshal fails to decode a plugin's hook response (hooks.Response) from the bytes returned by RunHook. The raw response text and the underlying json error are wrapped for diagnostics. A misbehaving plugin that returns non-JSON or a structurally wrong payload triggers it.

Solutions

  1. Ensure the plugin writes only the JSON Response payload to stdout (send logs to stderr).
  2. Validate the response against the hooks.Response schema (Type and Template fields).
  3. Update the plugin to the current hook protocol version.
Defensive patterns

Strategy: try-catch

Validate before calling

if !json.Valid(resp) {
    return fmt.Errorf("plugin %s returned non-JSON hook response", pluginName)
}

Try / catch

var message hooks.Response
if err := json.Unmarshal(resp, &message); err != nil {
    logrus.WithError(err).WithField("plugin", pluginName).Debug("skipping misbehaving plugin")
    continue // the manager already skips plugins on error
}

Prevention

When it happens

Trigger: A plugin hook executable that prints non-JSON to stdout, or emits JSON that does not match the hooks.Response schema (missing/extra fields, wrong types).

Common situations: Plugin writing debug/logs to stdout instead of stderr, returning a partial JSON object, or an older plugin using an incompatible response format.

Related errors


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

Appendix: source

Thrown at cli-plugins/manager/hooks.go:91

		}

		p, err := getPlugin(pluginName, pluginDirs, rootCmd)
		if err != nil {
			return nil, false, err
		}

		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 {

View on GitHub (pinned to 4f84911bfe)