pulumi/pulumi · error
hook %s: command elements must be strings was %v
Error message
hook %s: command elements must be strings was %v
What it means
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.
Source
Thrown at pkg/pcl/runtime/interpreter.go:290
// runCommand evaluates the hook's command with the given `args` and runs it. The first
// return value is the error from running the command, if any; the second is an error
// evaluating the command expression itself.
runCommand := func(ctx context.Context, args map[string]cty.Value) (error, error) {
evalCtx := i.evalContext.NewChild()
evalCtx.SetVariable("args", cty.ObjectVal(args))
cmdVal, _, evalDiags := evalCtx.Evaluate(cmdExpr)
if evalDiags.HasErrors() {
return nil, fmt.Errorf("hook %s: evaluating command: %v", hookName, evalDiags)
}
if !cmdVal.IsArray() {
return nil, fmt.Errorf("hook %s: command must be a list of strings", hookName)
}
var cmdArgs []string
for _, arg := range cmdVal.ArrayValue() {
arg, _ = unwrapOutputs(arg)
if !arg.IsString() {
return nil, fmt.Errorf("hook %s: command elements must be strings was %v", hookName, arg)
}
cmdArgs = append(cmdArgs, arg.StringValue())
}
if len(cmdArgs) == 0 {
return nil, fmt.Errorf("hook %s: command must not be empty", hookName)
}
cmd := exec.CommandContext(ctx, cmdArgs[0], cmdArgs[1:]...)
cmd.Dir = workingDir
if out, runErr := cmd.CombinedOutput(); runErr != nil {
return fmt.Errorf("hook command %v failed: %s\n%s", cmdArgs, runErr, out), nil
}
return nil, nil
}
if h.Kind == pcl.HookKindError {
// Error hooks return whether the failed operation should be retried: retry if and
// only if the command exits successfully.View on GitHub (pinned to 793f7b2e16)
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
Example fix
// before
command: ["curl", port]
// after
command: ["curl", "${port}"] Defensive patterns
Strategy: validation
Validate before calling
cmd.forEach((arg, i) => { if (typeof arg !== "string") throw new Error(`hook command element ${i} is not a string: ${JSON.stringify(arg)}`); }); Type guard
function isString(x) { return typeof x === "string"; } Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- hook %s: command must be a list of strings
- hooks must be an object mapping hook types to command lists
- hooks.%s must be an array of hooks
- invoke options dependsOn must be a list of strings
- invoke options dependsOn must be a list of resource objects
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/4fb4a061ee534645.
Report an issue: GitHub.