Pulumi Error: Error Thrown Inside a ComponentResource Constructor
Fix Pulumi 'exception in ComponentResource constructor' and missing registerOutputs errors: diagnose thrown constructor code, super() ordering, and unresolved child outputs.
- #pulumi
- #iac
- #troubleshooting
- #errors
Stuck on this Pulumi error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Exact Error Message
error: Running program '/home/ci/app' failed with an unhandled exception:
<ref *1> Error: Cannot read properties of undefined (reading 'id')
at new MyService (/home/ci/app/service.ts:24:41)
at Object.<anonymous> (/home/ci/app/index.ts:6:17)
error: an unhandled error occurred: Program exited with non-zero exit code: 1
You may also see the resource stuck reporting registerResourceOutputs never completing, or a warning such as Component resource was not registered; did you call registerOutputs? when a ComponentResource subclass throws before it finishes wiring up its children.
What It Means
A ComponentResource is a logical container you build in your program’s language (TypeScript, Python, Go, .NET). Pulumi calls your subclass constructor to create the child resources, then expects you to publish the component’s outputs with this.registerOutputs(...). If any line inside that constructor throws, the whole pulumi up/pulumi preview aborts with an unhandled exception, because your program crashed before the engine could finish registering the component tree.
This is application code failing, not the Pulumi engine or a cloud provider rejecting a request. The stack trace points at your file and line, so the fix is almost always in the constructor body: an undefined value, a missing parent option, or a registerOutputs call that never runs.
Common Causes
- Dereferencing a value that is still an
Output<T>(orAwaitable) as if it were a plain string, e.g. readingbucket.idsynchronously. - The constructor throws before reaching
this.registerOutputs({...}), leaving the component half-built. - Forgetting to call
super("pkg:module:MyService", name, {}, opts)first, sothisis not initialized when you create children. - Child resources not passed
{ parent: this }, causing them to register outside the component and orphaning outputs. - A synchronous exception (bad config lookup, null map access, failed
require) inside the constructor.
Diagnostic Commands
Re-run with full detail to see the exact throwing line and stack:
pulumi up --logtostderr --logflow -v=9 2>pulumi.log
Preview only, so you can iterate without mutating cloud state:
pulumi preview --diff
Confirm which stack and config the program is reading (bad config often triggers the throw):
pulumi stack --show-name
pulumi config
For TypeScript programs, type-check the constructor independently of Pulumi:
npx tsc --noEmit
Step-by-Step Resolution
-
Read the stack trace top-down and open the file:line it names. The first frame inside your code is the real culprit, not the Pulumi library frames beneath it.
-
Call
super()as the very first statement, before creating any children:
export class MyService extends pulumi.ComponentResource {
constructor(name: string, args: MyServiceArgs, opts?: pulumi.ComponentResourceOptions) {
super("myorg:app:MyService", name, {}, opts);
// create children AFTER super()
}
}
- Parent every child to the component so its outputs register correctly:
const bucket = new aws.s3.Bucket(`${name}-bucket`, {}, { parent: this });
- Never treat an
Output<T>as a raw value inside the constructor. Chain with.apply()instead of reading it directly:
this.url = bucket.bucketRegionalDomainName.apply(d => `https://${d}`);
- Always finish the constructor with
registerOutputs, even if empty, so the engine knows the component is complete:
this.registerOutputs({ url: this.url });
}
}
- Re-run and confirm the component and its children appear in the plan:
pulumi preview --diff
+ myorg:app:MyService my-service create
+ └─ aws:s3:Bucket my-service-bucket create
Prevention
- Put
super(...)first andthis.registerOutputs(...)last in everyComponentResourceconstructor as a fixed template. - Keep constructor logic pure: no top-level
awaiton cloud calls, no synchronous reads ofOutput<T>. - Pass
{ parent: this }to every child resource so the component tree is coherent. - Run
tsc --noEmit(or the equivalent build for Go/.NET/Python type checks) in CI beforepulumi preview. - Validate and default your
argsat the top of the constructor so a missing field fails with a clear message, not a deepundefined. - Reach for ready-made component scaffolding from the Pulumi prompt library to get the
super/registerOutputsboilerplate right the first time.
Related Errors
resource registration ... timed out— a hang, not a thrown exception, usually a blocked async call.Component resource was not registered; did you call registerOutputs?— the constructor returned without registering outputs.TypeError: Cannot read properties of undefined— the most common underlying throw inside a constructor.unknown resource type— a child resource whose provider plugin is not installed.
Frequently Asked Questions
Why does my whole pulumi up fail when only one component throws? Because your program runs as a single process; an unhandled exception anywhere aborts the process before the engine can complete the deployment, so nothing after the throw is applied.
Do I have to call registerOutputs if my component has no outputs? Yes, call this.registerOutputs({}). It signals to the engine that the component is fully constructed; skipping it can leave the component in a partially registered state.
Why is bucket.id undefined inside my constructor? During preview, resource IDs are unknown, and even at update time id is an Output<T>, not a string. Use .apply() or pulumi.interpolate instead of reading it synchronously.
Does parent: this really matter? Yes. Without it, child resources register outside the component, so the component’s dependency graph and outputs are wrong even if the program does not throw.
Where do I set the component options like providers or protect? Pass them through the opts argument to super(); they propagate to children that use { parent: this }. For more component and provider patterns, see the Pulumi guides.
Fixed it? Get 500 Pulumi & 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.
Did this fix your issue?
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.