{"record":{"id":"8d2f8c9c930e8492","repo":"pulumi/pulumi","slug":"hooks-must-be-an-object-mapping-hook-types-to-comm","errorCode":null,"errorMessage":"hooks must be an object mapping hook types to command lists","messagePattern":"hooks must be an object mapping hook types to command lists","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/pcl/runtime/interpreter.go","lineNumber":1866,"sourceCode":"\t\t\t\t\t}\n\t\t\t\t\thdopt = append(hdopt, v.StringValue())\n\t\t\t\t}\n\t\t\t\trequest.HideDiffs = hdopt\n\t\t\t}\n\t\t}\n\n\t\t// Process hooks - register command hooks and build the hooks binding\n\t\tif res.Options.Hooks != nil {\n\t\t\thooksVal, poison, diags := evalCtx.Evaluate(res.Options.Hooks)\n\t\t\tif poison != nil {\n\t\t\t\treturn makePoisonValue(*poison), nil\n\t\t\t}\n\t\t\tif diags.HasErrors() {\n\t\t\t\treturn cty.NilVal, diags\n\t\t\t}\n\t\t\tif !hooksVal.IsNull() && !hooksVal.IsComputed() {\n\t\t\t\tif !hooksVal.IsObject() {\n\t\t\t\t\treturn cty.NilVal, errors.New(\"hooks must be an object mapping hook types to command lists\")\n\t\t\t\t}\n\t\t\t\tbinding := &pulumirpc.RegisterResourceRequest_ResourceHooksBinding{}\n\t\t\t\tfor hookType, commandLists := range hooksVal.ObjectValue() {\n\t\t\t\t\tif commandLists.IsNull() || commandLists.IsComputed() {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif !commandLists.IsArray() {\n\t\t\t\t\t\treturn cty.NilVal, fmt.Errorf(\"hooks.%s must be an array of hooks\", hookType)\n\t\t\t\t\t}\n\t\t\t\t\tvar hookNames []string\n\t\t\t\t\tfor idx, hookVal := range commandLists.ArrayValue() {\n\t\t\t\t\t\tif hookVal.IsNull() || hookVal.IsComputed() {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !hookVal.IsString() {\n\t\t\t\t\t\t\treturn cty.NilVal, fmt.Errorf(\"hooks.%s[%d] must be a reference to a named hook block\", hookType, idx)\n\t\t\t\t\t\t}\n\t\t\t\t\t\thookNames = append(hookNames, hookVal.StringValue())","sourceCodeStart":1848,"sourceCodeEnd":1884,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/pkg/pcl/runtime/interpreter.go#L1848-L1884","documentation":"The resourceHooks option must be an object mapping hook types (e.g. beforeCreate, afterUpdate) to arrays of hook names. This error is thrown when the evaluated hooks value is not a cty object (e.g. a string or array), before populating the ResourceHooksBinding.","triggerScenarios":"Passing options {resourceHooks: \"my-hook\"} or a list instead of an object in a PCL program (interpreter.go:1866).","commonSituations":"Specifying a single hook without the hook-type wrapper object; copying YAML hooks config directly as a list; confusing hook names with the hook-type map.","solutions":["Structure the value as an object: options { resourceHooks: { beforeCreate: [\"my-hook\"] } }","Use valid hook-type keys mapping to arrays of hook names","Ensure the value is an object literal, not a string or list"],"exampleFix":"// before\noptions = { resourceHooks: \"my-hook\" }\n// after\noptions = { resourceHooks: { beforeCreate: [\"my-hook\"] } }","handlingStrategy":"validation","validationCode":"if h, ok := opts[\"resourceHooks\"]; ok && h != nil {\n    if _, isMap := h.(map[string]any); !isMap {\n        return fmt.Errorf(\"resourceHooks must be an object of hook type -> hook list\")\n    }\n}","typeGuard":"func isHookBinding(v any) bool {\n    _, ok := v.(map[string]any)\n    return ok\n}","tryCatchPattern":null,"preventionTips":["Always wrap hooks in an object keyed by hook type (beforeCreate, afterUpdate, ...)","Convert YAML hook lists into the hook-type map form when porting","Validate the shape of the hooks option before running the program"],"tags":["pcl","resource-options","hooks","type-validation"],"backgroundTag":"invalid-option-type","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}