Modern Angular
NG0950: Reading a Required Input Before Angular Sets It
NG0950: Required input is accessed before a value is set fires when you read an input.required() signal too early. The docs are exact: inputs are guaranteed available in ngOnInit and after, not in the constructor. So read required inputs in ngOnInit, a computed, or an effect, or fix the parent that never bound them.
The error, in one line
The full message is NG0950: Required input is accessed before a value is set, and the official description is one sentence: a required input was accessed but no value was bound. Required signal inputs, declared with input.required<T>(), have no initial value by design, so reading one before the parent's binding has been applied has nothing to return, and Angular throws instead of handing you undefined.
Signal inputs landed in Angular 17.1 (developer preview at the time), and input.required() is the variant with no fallback. That is the whole appeal: the type is T, never T | undefined, and the framework guarantees a value is present once the component is wired up. NG0950 is the framework keeping that promise honest. You read the value at a moment when the guarantee does not yet hold, and it tells you so rather than letting a silent undefined leak through your types.
When inputs are actually available
The docs state the timing plainly: inputs are guaranteed to be available in the ngOnInit lifecycle hook and afterwards. The constructor runs before Angular applies bindings, and field initializers run as part of construction, so both execute while the required input is still empty. Read it there and you are reading before the value exists.
This is the part that trips people who came from the decorator era. With @Input(), a field was just undefined until set, and reading it early returned undefined quietly. A required signal input has no such middle state, so the same early read that used to pass silently now throws. The fix is never to weaken the input; it is to read it at a point where the binding has run.
| Where you read it | Has the binding run? | Safe to read a required input? |
|---|---|---|
| Field initializer | No, runs during construction | No, throws NG0950 |
| constructor | No, runs before bindings apply | No, throws NG0950 |
| computed(() => ...) | Yes, the derivation runs on first read | Yes |
| effect(() => ...) | Yes, runs during change detection | Yes |
| ngOnInit and later hooks | Yes, guaranteed available | Yes |
| Template expression | Yes, evaluated during change detection | Yes |
Two causes that look identical
NG0950 has exactly two roots, and the message is the same for both, which is why it confuses. The first is timing: the binding is there in the parent, but your code reads the input before it has been applied. The docs name the common shape directly, that this is commonly happening when the input is read as part of class construction. The fix is to move the read.
The second is a missing binding: the parent template forgot the attribute, so no value was ever bound and there is nothing to wait for. Here, moving the read changes nothing, because the input stays empty for the component's whole life. Before you restructure lifecycle code, confirm the parent actually passes the input. The two causes need opposite fixes, so naming which one you have is the first real step.
@Component({ selector: 'order-summary', /* ... */ })
export class OrderSummary {
// Required input: type is Order, never Order | undefined
readonly order = input.required<Order>();
// Throws NG0950: the field initializer runs during construction,
// before Angular applies the parent's [order] binding
readonly totalAtInit = this.order().total;
// Safe: computed runs on first read, after the binding is applied
readonly total = computed(() => this.order().total);
constructor() {
// Throws NG0950: constructor runs before bindings apply
console.log(this.order().id);
// Safe: the effect runs during change detection, after binding
effect(() => console.log('order changed', this.order().id));
}
ngOnInit() {
// Safe: inputs are guaranteed available here and afterwards
this.loadHistory(this.order().id);
}
}The fix the docs give, and the one they don't
For a real timing bug, the documented fixes are two. Read the input in a reactive context: the template, a computed, or an effect, all of which run after binding. Or read it in ngOnInit or later. A computed off a required input is the move I reach for most, because it expresses the derivation once and stays correct as the input changes, with no lifecycle hook to remember.
There is a third fix the error page does not mention, because it is a design decision, not a timing fix. If "no value yet" is a genuine state your component can be in, the input should not be required at all. Give it a default with input<T>(fallback) and drop .required. Then an unbound parent is valid, the early read returns the default, and NG0950 cannot fire. Reaching for that is a judgment call: make the input required when a missing value is a programming error you want caught at build time, and make it optional with a default when absence is a state the component is meant to render.
Fixing the missing binding in the parent
When the cause is the second one, the work is in the parent template, not the child. Angular already enforces required inputs at build time when the component is used in a template, so a flatly missing binding usually surfaces as a compile error first. NG0950 at runtime tends to mean the binding exists but resolves to nothing useful, or the element is created through a path the template checker does not cover, such as a dynamic createComponent call.
For a dynamically created component, the binding does not come from a template, so nothing forces it on you. You have to set the input yourself on the created component reference, and set it before anything reads it. The same rule holds: the value must be in place by the time ngOnInit or the first computed/effect read runs.
// Template path: the compiler enforces [order]; omit it and the
// build fails before NG0950 can ever run
// <order-summary [order]="selected()" />
// Dynamic path: no template, so you set the input by hand
const ref = this.container.createComponent(OrderSummary);
ref.setInput('order', this.selected());
// setInput before change detection reads the value; otherwise
// the first read still hits an unset required input and throwsReusable artifact
Where it is safe to read a required input
- Field initializer or constructor: never read a required input here; both run before binding and throw NG0950.
- Need the value once, on init: read it in ngOnInit or a later hook, where inputs are guaranteed available.
- Need a value derived from it: use computed(() => input()); the derivation runs after binding and tracks changes.
- Need a side effect on its value: use effect(() => ...); it runs during change detection, after the binding.
- Absence is a real state to render: drop .required and use input<T>(default) instead of fixing lifecycle timing.
- Error persists with the read moved: it is a missing binding; fix the parent template or setInput on the dynamic component.
FAQ
What does NG0950 mean in Angular?
It means a required signal input, declared with input.required(), was read before Angular bound a value to it. Required inputs have no initial value, so reading one before the parent binding is applied has nothing to return and Angular throws instead of yielding undefined.
Why does reading a required input in the constructor throw NG0950?
The constructor and field initializers run as part of constructing the component, before Angular applies the parent's bindings. The docs guarantee inputs are available only in ngOnInit and afterwards, so a constructor read happens while the required input is still empty.
Where is it safe to read a required input?
Inside the template, a computed, or an effect, all of which run after binding, or inside ngOnInit or a later lifecycle hook. A computed off the required input is a common choice because it derives once and stays correct as the input changes.
Should I just give the input a default value?
Only if 'no value yet' is a real state your component should render. Then use input<T>(default) and drop required. If a missing value is a programming error you want caught at build time, keep it required and move the read to a safe point instead.
Sources checked
- https://angular.dev/errors/NG0950
- https://angular.dev/api/core/input
- https://angular.dev/guide/components/inputs
- https://angular.dev/guide/signals
- https://angular.dev/guide/components/lifecycle
- https://angular.dev/api/core/computed
- https://angular.dev/api/core/effect
- https://github.com/angular/angular/releases/tag/17.1.0
Modern Angular Playbook
This article is one play.
The Modern Angular Playbook collects the diagnostic, the adoption matrix, eleven plays, and the 30-day plan. Free, in English and Portuguese.
You get both PDFs by email, through the Dojo IA list.
André Ramosavailable for remote roles, UTC−3Get in touch →