Pulumi Error: 'unmarshalling properties: expected a value of type but got' — Cause, Fix, and Troubleshooting Guide
Fix Pulumi's 'unmarshalling properties: unmarshalling value: expected a value of type but got' error caused by a type mismatch in resource inputs or state.
- #pulumi
- #iac
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
error: unmarshalling properties means Pulumi passed a resource’s inputs or outputs across the wire between the engine, your Python program, and the provider — and the value it received did not match the type the provider’s schema expects. The provider (for example pulumi-aws) is strongly typed; when you hand it a string where it wants a number, a list where it wants a single object, or None where a value is required, decoding fails.
The literal message names the expected and actual types, which is your primary clue:
error: unmarshalling properties: unmarshalling value: expected a value of type string but got number
This is almost always a bug in how a resource argument is built in your Python code, though it can also come from stale or hand-edited state, or from feeding one resource’s output into an incompatible input.
Symptoms
pulumi previeworpulumi upfails during resource registration withunmarshalling properties.- The message spells out a type pair, e.g.
expected a value of type array but got objectorexpected a value of type bool but got string. - The error points at a specific resource type/URN (an
aws:s3/bucket:Bucket,aws:ec2/instance:Instance, etc.). - It started after you changed an argument, upgraded a provider SDK, or imported/refreshed state.
- The same program worked before a
pulumi-awsmajor-version bump.
Common Root Causes
1. Passing the wrong Python type for an argument
The most common case: an argument wants a number, a bool, or a nested object, but your code supplies a string (or vice versa). Values that come from os.environ, pulumi.Config().get(), or JSON are strings by default.
import os
import pulumi_aws as aws
# WRONG: port comes back as a string "8080"
port = os.environ["APP_PORT"]
# The provider expects an int for from_port/to_port
aws.ec2.SecurityGroupRule(
"app",
type="ingress",
from_port=port, # <-- string, provider wants a number
to_port=port,
protocol="tcp",
cidr_blocks=["0.0.0.0/0"],
security_group_id=sg.id,
)
2. A list vs. single value (or object vs. array) mismatch
Some arguments take a single object, others take a list of them. Wrapping (or not wrapping) in a list produces expected a value of type array but got object.
# WRONG: tags must be a dict/map, not a list
aws.s3.BucketV2("data", tags=[{"Name": "data"}]) # object expected, array given
# RIGHT
aws.s3.BucketV2("data", tags={"Name": "data"})
3. A provider SDK upgrade changed a property’s shape
Major-version provider upgrades (for example pulumi-aws v5 to v6) rename or retype properties. State written by the old SDK, or code written for it, can now decode into an incompatible type.
expected a value of type object but got array
4. Corrupted or hand-edited state
If someone edited the stack’s state (via pulumi state surgery, a bad import, or manual JSON edits) so a stored value no longer matches the schema, reading that state back fails to unmarshal.
5. Feeding an Output of the wrong type into an input
Chaining resource.some_output into an argument that expects a different type (e.g. an ARN where an ID is expected, or a whole object where a string field is expected) surfaces here once the value resolves.
How to Diagnose
Read the type pair in the message first — expected X but got Y tells you exactly what to correct. Then get detail:
# Verbose engine logs show the resource + payload being decoded
pulumi up --logtostderr -v=9 2>diag.log
Identify which resource is failing and inspect the exact inputs Pulumi will send:
pulumi preview --diff
Check the provider version in play, since upgrades are a frequent trigger:
pulumi plugin ls
grep -i pulumi-aws requirements.txt
Confirm the expected type for the argument by checking the property in the Pulumi Registry docs for that resource, then compare against what your Python builds:
# Print the runtime type of the value you're passing
python3 -c "import os; print(type(os.environ.get('APP_PORT')))"
Inspect stored state if you suspect corruption:
pulumi stack export > stack.json
# search stack.json for the failing resource URN and check the property value
Fixes
Coerce the value to the type the provider expects: Cast strings to int/float/bool before passing them in.
import os
import pulumi_aws as aws
port = int(os.environ["APP_PORT"]) # cast to int
aws.ec2.SecurityGroupRule(
"app",
type="ingress",
from_port=port,
to_port=port,
protocol="tcp",
cidr_blocks=["0.0.0.0/0"],
security_group_id=sg.id,
)
Match list vs. object shape to the schema: Use the Pulumi Registry page for the resource to confirm whether an argument is a map, a single object, or a list, then wrap accordingly.
# tags is a map
aws.s3.BucketV2("data", tags={"Name": "data", "Env": "prod"})
# an argument documented as a list of objects
aws.ec2.Instance(
"web",
ebs_block_devices=[{"device_name": "/dev/sdb", "volume_size": 50}],
# ... other args
)
Handle Config typing explicitly: Config returns strings unless you use the typed getters, which convert and validate for you.
import pulumi
config = pulumi.Config()
port = config.require_int("port") # returns an int
enabled = config.require_bool("enabled")
sizes = config.require_object("sizes") # returns a parsed structured value
After a provider upgrade, refresh and re-run: Align state with the new schema, and update code to the new property names/shapes from the changelog.
pulumi refresh --yes
pulumi preview
Repair bad state as a last resort: Export, correct the offending value to the right type, and re-import.
pulumi stack export > stack.json
# edit stack.json so the value matches the expected type
pulumi stack import --file stack.json
What to Watch Out For
- Values from environment variables, CLI flags, and JSON files are strings — cast them before handing them to a provider.
- Prefer
Config.require_int/require_bool/require_objectoverget()+ manual parsing. - Read the provider’s major-version upgrade guide before bumping
pulumi-aws; property retypes are common. - Never hand-edit exported state unless you must; a wrong type there just moves the failure to read time.
- When chaining outputs into inputs, confirm the output is the field you think it is (
.idvs.arnvs the whole object).
Related Guides
- Pulumi Error: ‘invalid configuration key’ — Troubleshooting Guide
- Pulumi Error: ‘resource monitor shut down’ — Troubleshooting Guide
- Pulumi Error: ‘update failed’ — Troubleshooting Guide
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.