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

  1. Quote or stringify each element, e.g. use "${port}" instead of the bare number port
  2. Inspect the reported element value (%v in the message) and fix its type at the source
  3. 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

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


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/4fb4a061ee534645. Report an issue: GitHub.