sindresorhus/is · error · TypeError

Expected value which is `null or undefined`, received value

Error message

Expected value which is `null or undefined`, received value of type `${is(value)}`.

What it means

Thrown by `assertNullOrUndefined()` (source/index.ts:1797) when the value is neither `null` nor `undefined` — i.e. something IS present where the code expected nothing. Falsy values like `0`, `''`, `false`, and `NaN` still fail because they are real values.

Source

Thrown at source/index.ts:1799

}

export function assertNonNegativeNumber(value: unknown, message?: string): asserts value is number {
	if (!isNonNegativeNumber(value)) {
		throw new TypeError(message ?? typeErrorMessage('non-negative number', value));
	}
}

// eslint-disable-next-line @typescript-eslint/no-restricted-types
export function assertNull(value: unknown, message?: string): asserts value is null {
	if (!isNull(value)) {
		throw new TypeError(message ?? typeErrorMessage('null', value));
	}
}

// eslint-disable-next-line @typescript-eslint/no-restricted-types
export function assertNullOrUndefined(value: unknown, message?: string): asserts value is null | undefined {
	if (!isNullOrUndefined(value)) {
		throw new TypeError(message ?? typeErrorMessage('null or undefined', value));
	}
}

export function assertNumber(value: unknown, message?: string): asserts value is number {
	if (!isNumber(value)) {
		throw new TypeError(message ?? typeErrorMessage('number', value));
	}
}

export function assertNumericString(value: unknown, message?: string): asserts value is `${number}` {
	if (!isNumericString(value)) {
		throw new TypeError(message ?? typeErrorMessage('string with a number', value));
	}
}

// eslint-disable-next-line @typescript-eslint/no-restricted-types
export function assertObject(value: unknown, message?: string): asserts value is object {
	if (!isObject(value)) {

View on GitHub ↗ (pinned to 7821031c66)

Solutions

  1. Find what set the value: this assert firing means cleanup or reset was skipped — clear the timer/handle/state (set it back to null) on the teardown path.
  2. If falsy values like `0` or `''` should count as absent for your logic, this is the wrong check — test the condition explicitly instead of asserting null/undefined.
  3. In tests, reset shared module state in beforeEach/afterEach to avoid cross-test leakage.

Example fix

// before
start() {
  assert.nullOrUndefined(this.timer); // throws on second start()
  this.timer = setInterval(tick, 1000);
}
// after
stop() {
  clearInterval(this.timer);
  this.timer = undefined; // reset so start() can assert cleanly
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (value === null || value === undefined) {
  assert.nullOrUndefined(value);
}

Type guard

function isNullOrUndefined(v: unknown): v is null | undefined {
  return v === null || v === undefined;
}

Try / catch

try {
  assert.nullOrUndefined(existing);
} catch (error) {
  if (error instanceof TypeError) {
    // existing holds a real value — you expected an unset slot; handle the already-set case
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling `assert.nullOrUndefined(value)` when the value holds any concrete value: a leftover handle/timer that should have been cleared, a falsy-but-real value like `0` or `''` assumed to count as 'empty', or state left over from a previous operation.

Common situations: Guarding against double-initialization ('this must not already be set') and hitting it because a singleton, listener, or timer wasn't torn down; assuming falsy means absent; test pollution where module-level state persists between tests.

Related errors


AI-assisted analysis of sindresorhus/is@7821031c66 (2026-07-31). Data as JSON: /data/errors/3b394cf1839bce15.json. Report an issue: GitHub ↗.