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 itHas the binding run?Safe to read a required input?
Field initializerNo, runs during constructionNo, throws NG0950
constructorNo, runs before bindings applyNo, throws NG0950
computed(() => ...)Yes, the derivation runs on first readYes
effect(() => ...)Yes, runs during change detectionYes
ngOnInit and later hooksYes, guaranteed availableYes
Template expressionYes, evaluated during change detectionYes

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.

The same input read too early vs read in the right placets
@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.

Binding the required input on a dynamically created componentts
// 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 throws

Reusable 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

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.

Open playbook →

André Ramosavailable for remote roles, UTC−3Get in touch →

Related

Note · 6 min

How I Would Adopt Signal Forms Now That They Are Stable in Angular 22

A production adoption filter for Signal Forms now that they are stable in Angular 22: use them for new forms, keep reactive forms where they already work.

Read article →

Guide · 8 min

OnPush Is the Default in Angular 22: What Breaks and What to Do

Why Angular 22 makes OnPush the default, what actually breaks, the two ways to fix it, and how the migration's Eager additions double as your cleanup backlog.

Read article →

Error · 9 min

NG0203: Why inject() Fails Outside an Injection Context

Why Angular throws NG0203 when inject() runs outside an injection context, the exact window where inject() is valid, the common traps, and the fixes ranked from field initializer to runInInjectionContext.

Read article →