{"id":"f2a5a3431d99da26","repo":"docker/cli","slug":"unexpected-hook-response-type","errorCode":null,"errorMessage":"unexpected hook response type: ","messagePattern":"unexpected hook response type: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cli-plugins/manager/hooks.go","lineNumber":96,"sourceCode":"\t\t}\n\n\t\tresp, err := p.RunHook(ctx, hooks.Request{\n\t\t\tRootCmd:      match,\n\t\t\tFlags:        flags,\n\t\t\tCommandError: cmdErrorMessage,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, false, err\n\t\t}\n\n\t\tvar message hooks.Response\n\t\tif err := json.Unmarshal(resp, &message); err != nil {\n\t\t\treturn nil, false, fmt.Errorf(\"failed to unmarshal hook response (%q): %w\", string(resp), err)\n\t\t}\n\n\t\t// currently the only hook type\n\t\tif message.Type != hooks.NextSteps {\n\t\t\treturn nil, false, errors.New(\"unexpected hook response type: \" + strconv.Itoa(int(message.Type)))\n\t\t}\n\n\t\tmessages, err = hooks.ParseTemplate(message.Template, subCmd)\n\t\tif err != nil {\n\t\t\treturn nil, false, err\n\t\t}\n\n\t\treturn messages, true, nil\n\t}\n\n\tfor pluginName, pluginCfg := range pluginsCfg {\n\t\tmessages, ok, err := tryInvokeHook(pluginName, pluginCfg)\n\t\tif err != nil {\n\t\t\t// skip misbehaving plugins, but don't halt execution\n\t\t\tlogrus.WithFields(logrus.Fields{\n\t\t\t\t\"error\":  err,\n\t\t\t\t\"plugin\": pluginName,\n\t\t\t}).Debug(\"Plugin hook invocation failed\")","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/docker/cli/blob/e9452d6e785f6e365712b9d71bd7517591773c86/cli-plugins/manager/hooks.go#L78-L114","documentation":"After invoking a plugin hook, the CLI unmarshals the plugin's JSON response into hooks.Response and checks its Type field. hooks.NextSteps is currently the only supported hook response type, so any other numeric type value is rejected with this error and the hook's messages are discarded. It exists to keep forward-compatibility explicit: unknown types fail loudly rather than being misrendered.","triggerScenarios":"A plugin's hook binary (invoked via RunHook) returns JSON whose 'type' field is not the NextSteps constant — e.g. a plugin built against a newer hooks schema that defines additional types, or a plugin that emits a zero/omitted type due to a struct field mismatch, running under an older docker/cli.","commonSituations":"Version skew between a CLI plugin (buildx, scout, compose) and the docker CLI; a custom plugin author forgetting to set Type: hooks.NextSteps in the response; a plugin printing non-conforming JSON to stdout during the hook invocation.","solutions":["Set Type to hooks.NextSteps in the plugin's hook response struct before marshaling","Upgrade the docker CLI so it recognizes the response type the plugin emits","Ensure the plugin writes only the JSON response to stdout during the hook call (no stray log output altering the payload)","Rebuild the plugin against the same docker/cli cli-plugins/hooks package version as the installed CLI"],"exampleFix":"// before (plugin hook handler)\nresp := hooks.Response{Template: msg} // Type left as zero value\n// after\nresp := hooks.Response{Type: hooks.NextSteps, Template: msg}","handlingStrategy":null,"validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":["docker-cli","cli-plugins","hooks","version-skew","json"],"analyzedSha":"e9452d6e785f6e365712b9d71bd7517591773c86","analyzedAt":"2026-08-01T07:09:22.474Z","schemaVersion":2}