pulumi/pulumi · error

cannot marshal Input that is not a struct or map, saw type %

Error message

cannot marshal Input that is not a struct or map, saw type %s

What it means

marshalInputs in rpc.go can only project Input values whose Go type is a struct or a map (it iterates fields or keys to build RPC properties). Any other Input kind (scalars, slices) reaches the default branch and is rejected, because there is no generic way to map it to the gRPC property structure.

Source

Thrown at sdk/go/pulumi/rpc.go:210

			err := marshalProperty(tag, pv.Field(i).Interface(), destField.Type)
			if err != nil {
				return nil, nil, nil, err
			}
		}
	case reflect.Map:
		ktype := rt.Key()
		contract.Assertf(ktype.Kind() == reflect.String,
			"expected map with string keys, got %v (%v)", ktype, ktype.Kind())
		for _, key := range pv.MapKeys() {
			keyname := key.Interface().(string)
			val := pv.MapIndex(key).Interface()
			err := marshalProperty(keyname, val, rt.Elem())
			if err != nil {
				return nil, nil, nil, err
			}
		}
	default:
		return nil, nil, nil, fmt.Errorf("cannot marshal Input that is not a struct or map, saw type %s", pt.String())
	}

	urns := slice.Prealloc[URN](len(deps))
	for v := range deps {
		urns = append(urns, v)
	}
	return pmap, pdeps, urns, nil
}

// `gosec` thinks these are credentials, but they are not.
//
//nolint:gosec
const rpcTokenUnknownValue = "04da6b54-80e4-46f7-96ec-b56ff0331ba9"

const cannotAwaitFmt = "cannot marshal Output value of type %T; please use Apply to access the Output's value"

// marshalInput marshals an input value, returning its raw serializable value along with any dependencies.
func marshalInput(v any, destType reflect.Type) (resource.PropertyValue, []Resource, error) {

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Pass the proper Inputs struct type for the resource (e.g. &MyResourceArgs{...}) rather than a scalar Input
  2. Wrap keyed values in a Map/struct Input before marshalling
  3. If implementing provider plumbing, use marshalInputOptions per property instead of passing scalars to marshalInputs

Example fix

// before
props, _, _, err := marshalInputs(pulumi.String("x"), opts)
// after
props, _, _, err := marshalInputs(&MyResourceArgs{Name: pulumi.String("x")}, opts)
Defensive patterns

Strategy: type-guard

Validate before calling

func isStructOrMapInput(v any) bool {
    t := reflect.TypeOf(v)
    if t == nil {
        return false
    }
    for t.Kind() == reflect.Pointer {
        t = t.Elem()
    }
    return t.Kind() == reflect.Struct || t.Kind() == reflect.Map
}

Prevention

When it happens

Trigger: Passing a value whose dynamic type is not a struct or map Input into marshalInputs, e.g. calling lower-level registration APIs or custom providers that hand raw Input values (like pulumi.StringArray or a StringInput) where a struct/map Inputs value is expected.

Common situations: Custom provider authors building Call/Construct plumbing who accidentally pass a scalar Input as the property bag; misuse of internal marshalling APIs in tests.

Related errors


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