{"record":{"id":"4fb4a061ee534645","repo":"pulumi/pulumi","slug":"hook-s-command-elements-must-be-strings-was-v","errorCode":null,"errorMessage":"hook %s: command elements must be strings was %v","messagePattern":"hook (.+?): command elements must be strings was (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/pcl/runtime/interpreter.go","lineNumber":290,"sourceCode":"\t// runCommand evaluates the hook's command with the given `args` and runs it. The first\n\t// return value is the error from running the command, if any; the second is an error\n\t// evaluating the command expression itself.\n\trunCommand := func(ctx context.Context, args map[string]cty.Value) (error, error) {\n\t\tevalCtx := i.evalContext.NewChild()\n\t\tevalCtx.SetVariable(\"args\", cty.ObjectVal(args))\n\n\t\tcmdVal, _, evalDiags := evalCtx.Evaluate(cmdExpr)\n\t\tif evalDiags.HasErrors() {\n\t\t\treturn nil, fmt.Errorf(\"hook %s: evaluating command: %v\", hookName, evalDiags)\n\t\t}\n\t\tif !cmdVal.IsArray() {\n\t\t\treturn nil, fmt.Errorf(\"hook %s: command must be a list of strings\", hookName)\n\t\t}\n\t\tvar cmdArgs []string\n\t\tfor _, arg := range cmdVal.ArrayValue() {\n\t\t\targ, _ = unwrapOutputs(arg)\n\t\t\tif !arg.IsString() {\n\t\t\t\treturn nil, fmt.Errorf(\"hook %s: command elements must be strings was %v\", hookName, arg)\n\t\t\t}\n\t\t\tcmdArgs = append(cmdArgs, arg.StringValue())\n\t\t}\n\t\tif len(cmdArgs) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"hook %s: command must not be empty\", hookName)\n\t\t}\n\n\t\tcmd := exec.CommandContext(ctx, cmdArgs[0], cmdArgs[1:]...)\n\t\tcmd.Dir = workingDir\n\t\tif out, runErr := cmd.CombinedOutput(); runErr != nil {\n\t\t\treturn fmt.Errorf(\"hook command %v failed: %s\\n%s\", cmdArgs, runErr, out), nil\n\t\t}\n\t\treturn nil, nil\n\t}\n\n\tif h.Kind == pcl.HookKindError {\n\t\t// Error hooks return whether the failed operation should be retried: retry if and\n\t\t// only if the command exits successfully.","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/pkg/pcl/runtime/interpreter.go#L272-L308","documentation":"After confirming the hook command is an array, the interpreter unwraps each element (resolving Output values) and requires each to be a string. This error is thrown when an element of the command list is not a string. It exists because exec argv elements must be strings.","triggerScenarios":"A command list containing non-string elements, e.g. [\"echo\", 42], [\"arg\", true], or an element that unwraps to a non-string cty value (number, bool, object).","commonSituations":"Interpolating a number into an argument without converting to string; accidentally passing an object or list as an argument element; binding an output whose resolved value is not a string.","solutions":["Quote or stringify each element, e.g. use \"${port}\" instead of the bare number port","Inspect the reported element value (%v in the message) and fix its type at the source","If the value comes from an output, ensure it resolves to a string"],"exampleFix":"// before\ncommand: [\"curl\", port]\n// after\ncommand: [\"curl\", \"${port}\"]","handlingStrategy":"validation","validationCode":"cmd.forEach((arg, i) => { if (typeof arg !== \"string\") throw new Error(`hook command element ${i} is not a string: ${JSON.stringify(arg)}`); });","typeGuard":"function isString(x) { return typeof x === \"string\"; }","tryCatchPattern":null,"preventionTips":["Stringify numbers/bools explicitly when building arguments (\"${port}\")","Avoid passing raw outputs into command elements; coerce to string first","Unit-test hook command construction with representative output values"],"tags":["pcl","hooks","type-validation"],"backgroundTag":"hook-command-must-be-string-list","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}