pulumi/pulumi · error
props must be a struct or map or a pointer to a struct or ma
Error message
props must be a struct or map or a pointer to a struct or map
What it means
When props is supplied to a resource read/lookup, the Go SDK reflects on its type: after dereferencing a pointer, it must be a struct or a map with string keys. Any other kind (slice, int, string, map with non-string keys, pointer-to-pointer, etc.) is rejected because the marshaller cannot convert it into the gRPC argument structure.
Source
Thrown at sdk/go/pulumi/context.go:1388
t, name string, id IDInput, props Input, resource CustomResource, packageRef string, opts ...ResourceOption,
) error {
if t == "" {
return errors.New("resource type argument cannot be empty")
} else if name == "" {
return errors.New("resource name argument (for URN creation) cannot be empty")
} else if id == nil {
return errors.New("resource ID is required for lookup and cannot be empty")
}
if props != nil {
propsType := reflect.TypeOf(props)
if propsType.Kind() == reflect.Pointer {
propsType = propsType.Elem()
}
//nolint:staticcheck // Not applying de-morgens law right now
if !(propsType.Kind() == reflect.Struct ||
(propsType.Kind() == reflect.Map && propsType.Key().Kind() == reflect.String)) {
return errors.New("props must be a struct or map or a pointer to a struct or map")
}
}
options := merge(opts...)
parent := options.Parent
if options.Parent == nil {
options.Parent = ctx.state.stack
}
// Before anything else, if there are transformations registered, give them a chance to run to modify the
// user-provided properties and options assigned to this resource.
var transformations []ResourceTransformation
var err error
props, options, transformations, err = applyTransformations(t, name, props, resource, opts, options)
if err != nil {
return err
}
View on GitHub (pinned to 793f7b2e16)
Solutions
- Pass a plain struct value/pointer or a map with string keys (e.g. map[string]interface{}).
- Dereference extra pointer levels before passing (props must be T, *T, **T will fail).
- Pass nil if the lookup needs no properties — the check only runs when props != nil.
Example fix
// before
props := []string{"a", "b"}
ctx.GetResource(tok, name, id, props)
// after
props := map[string]interface{}{"keys": []string{"a", "b"}}
ctx.GetResource(tok, name, id, props) Defensive patterns
Strategy: type-guard
Validate before calling
func validProps(p interface{}) bool {
if p == nil { return true }
t := reflect.TypeOf(p)
for t.Kind() == reflect.Pointer { t = t.Elem() }
return t.Kind() == reflect.Struct ||
(t.Kind() == reflect.Map && t.Key().Kind() == reflect.String)
} Type guard
func validProps(p interface{}) bool {
t := reflect.TypeOf(p)
for t != nil && t.Kind() == reflect.Pointer { t = t.Elem() }
if t == nil { return true }
return t.Kind() == reflect.Struct ||
(t.Kind() == reflect.Map && t.Key().Kind() == reflect.String)
} Try / catch
if err != nil {
if strings.Contains(err.Error(), "props must be a struct or map") {
return fmt.Errorf("bad props for %s lookup: %w", name, err)
}
return err
} Prevention
- Use typed structs or map[string]T for lookup props
- Strip extra pointer indirection before passing props
- Pass nil rather than an arbitrary value when props are not needed
When it happens
Trigger: Passing props like &someSlice, a map[int]string, a struct wrapped in a pointer inside another pointer, or a non-struct/map value to ctx.GetResource's props parameter.
Common situations: Passing a []T of results by mistake; using map[string]any via a typed alias with non-string key; double-pointer from an ORM/JSON decode helper.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- hook %s: onDryRun must be a boolean
- hook %s: ignoreErrors must be a boolean
- applier's %s input parameter must be assignable from %v, got
- cannot convert an input of type %T to a value of type %v
- result must be a map, but was a
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/19aff4ea59ee661f.
Report an issue: GitHub.