{"record":{"id":"47a8bb1940d15178","repo":"pulumi/pulumi","slug":"protect-must-be-a-boolean-or-null","errorCode":null,"errorMessage":"protect must be a boolean or null","messagePattern":"protect must be a boolean or null","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/pcl/runtime/interpreter.go","lineNumber":1593,"sourceCode":"\t\t\t\t}\n\t\t\t\trequest.IgnoreChanges = icopt\n\t\t\t}\n\t\t}\n\t\tif res.Options.Protect != nil {\n\t\t\tprotect, poison, diags := evalCtx.Evaluate(res.Options.Protect)\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 !protect.IsComputed() {\n\t\t\t\tvar popt *bool\n\t\t\t\tif protect.IsBool() {\n\t\t\t\t\tb := protect.BoolValue()\n\t\t\t\t\tpopt = &b\n\t\t\t\t} else if !protect.IsNull() {\n\t\t\t\t\treturn cty.NilVal, errors.New(\"protect must be a boolean or null\")\n\t\t\t\t}\n\t\t\t\trequest.Protect = popt\n\t\t\t}\n\t\t}\n\t\tif res.Options.ReplaceWith != nil {\n\t\t\treplaceWith, poison, diags := evalCtx.Evaluate(res.Options.ReplaceWith)\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 !replaceWith.IsNull() && !replaceWith.IsComputed() {\n\t\t\t\tif !replaceWith.IsArray() {\n\t\t\t\t\treturn cty.NilVal, errors.New(\"replaceWith must be an array of resources\")\n\t\t\t\t}\n\t\t\t\tvar rwopt []string\n\t\t\t\tfor _, v := range replaceWith.ArrayValue() {","sourceCodeStart":1575,"sourceCodeEnd":1611,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/pkg/pcl/runtime/interpreter.go#L1575-L1611","documentation":"The PCL interpreter evaluates the `protect` resource option and requires it to be a boolean or null. Any other concrete type (string \"true\", number, object) triggers this error and stops evaluation of the resource registration.","triggerScenarios":"Passing `options { protect = \"true\" }` (string) or any non-bool, non-null value in `__opts.protect` to registerResource in pkg/pcl/runtime.","commonSituations":"Users quoting booleans when converting from YAML/JSON configs (`protect: \"false\"`), or binding protect to a variable typed as string instead of bool.","solutions":["Use a bare boolean literal: protect = true or protect = false.","Unquote string booleans: \"true\" -> true.","Convert upstream strings to bools before passing (e.g. in the producing code or with a boolean-typed variable).","Pass null (or omit/leave computed) if protection is conditional at runtime."],"exampleFix":"// before (PCL)\noptions { protect = \"true\" }\n// after\noptions { protect = true }","handlingStrategy":"type-guard","validationCode":"func validateProtect(v any) error {\n\tswitch t := v.(type) {\n\tcase nil, bool:\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"protect must be bool, got %T\", t)\n\t}\n}","typeGuard":"func isBoolOrNull(v cty.Value) bool {\n\treturn v.IsNull() || v.Type() == cty.Bool\n}","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"protect must be a boolean or null\") {\n\treturn fmt.Errorf(\"check the 'protect' option: use unquoted true/false: %w\", err)\n}","preventionTips":["Never quote boolean options (\"true\" is invalid).","Use 0/1-free boolean types when migrating from YAML.","Keep protect bound to bool-typed variables only.","Omit or pass null instead of placeholders."],"tags":["pulumi","pcl","resource-options","type-error"],"backgroundTag":"invalid-resource-option-type","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}