pulumi/pulumi · error

malformed resource reference for %q: id not a string

Error message

malformed resource reference for %q: id not a string

What it means

In a resource reference, the 'id' field may be a plain string, a computed (unknown) value, or a known output wrapping a string. This error fires in the default branch of the ID switch: the ID was present but its property type is none of those accepted shapes (e.g. a number, bool, object, secret, or unknown output), so the deserializer cannot extract a resource ID string.

Source

Thrown at sdk/go/common/resource/plugin/rpc.go:529

			if typeProp, ok := obj["type"]; ok {
				if !typeProp.IsString() {
					return nil, fmt.Errorf("malformed resource reference for %q: type not a string", key)
				}
				typ = typeProp.StringValue()
			}

			id, hasID := "", false
			if idProp, ok := obj["id"]; ok {
				hasID = true
				switch {
				case idProp.IsString():
					id = idProp.StringValue()
				case idProp.IsComputed():
					// Leave the ID empty to indicate that it is unknown.
				case idProp.IsOutput():
					if idProp.OutputValue().Known {
						if !idProp.OutputValue().Element.IsString() {
							return nil, fmt.Errorf("malformed resource reference for %q: id not a string", key)
						}
						id = idProp.OutputValue().Element.StringValue()
					}
				default:
					return nil, fmt.Errorf("malformed resource reference for %q: id not a string", key)
				}
			}

			var packageVersion string
			if packageVersionProp, ok := obj["packageVersion"]; ok {
				if !packageVersionProp.IsString() {
					return nil, fmt.Errorf("malformed resource reference for %q: packageVersion not a string", key)
				}
				packageVersion = packageVersionProp.StringValue()
			}

			if !opts.KeepResources {
				value := urn.StringValue()

View on GitHub (pinned to 793f7b2e16)

Solutions

  1. Convert the resource ID to a string before serialization (e.g. strconv.FormatInt for numeric IDs).
  2. If the ID is unknown, serialize it as an unknown/computed value rather than a concrete non-string.
  3. If wrapped as a secret, use a secret-wrapped string so the element remains a string.
  4. Fix or remove the malformed id in exported state, then pulumi stack import.

Example fix

// before (producer)
m["id"] = resource.NewProperty(int64(12345))
// after
m["id"] = resource.NewProperty("12345")
Defensive patterns

Strategy: type-guard

Validate before calling

func validIDProp(p resource.Property) bool {
    return p.IsString() || p.IsComputed() ||
        (p.IsOutput() && (!p.OutputValue().Known || p.OutputValue().Element.IsString()))
}

Type guard

func asResourceID(p resource.Property) (string, bool) {
    switch {
    case p.IsString():
        return p.StringValue(), true
    case p.IsOutput() && p.OutputValue().Known && p.OutputValue().Element.IsString():
        return p.OutputValue().Element.StringValue(), true
    default:
        return "", false
    }
}

Prevention

When it happens

Trigger: obj["id"] is a non-string, non-computed, non-output property (number, bool, object, secret wrapper); a provider serializes an unknown ID as something other than a computed/unknown output.

Common situations: Custom providers storing numeric IDs (common in AWS-like providers) without converting to string; hand-edited state; serializers that wrap IDs in secrets; SDK version skew on how unknown IDs are represented.

Understand the failure class

Related errors


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