angular/components · error

Provided data source did not match an array, Observable, or

Error message

Provided data source did not match an array, Observable, or DataSource

What it means

The table's dataSource input was set to something that is neither an array, an Observable, nor an object implementing DataSource, so no data stream could be derived. In dev mode the table throws rather than rendering an empty table.

Source

Thrown at src/cdk/table/table.ts:1245

  /** Set up a subscription for the data provided by the data source. */
  private _observeRenderChanges() {
    // If no data source has been set, there is nothing to observe for changes.
    if (!this.dataSource) {
      return;
    }

    let dataStream: Observable<readonly T[]> | undefined;

    if (isDataSource(this.dataSource)) {
      dataStream = this.dataSource.connect(this);
    } else if (isObservable(this.dataSource)) {
      dataStream = this.dataSource;
    } else if (Array.isArray(this.dataSource)) {
      dataStream = observableOf(this.dataSource);
    }

    if (dataStream === undefined && (typeof ngDevMode === 'undefined' || ngDevMode)) {
      throw getTableUnknownDataSourceError();
    }

    this._renderChangeSubscription = combineLatest([dataStream!, this.viewChange])
      .pipe(takeUntil(this._onDestroy))
      .subscribe(([data, range]) => {
        this._data = data || [];
        this._renderedRange = range;
        this._dataStream.next(data);
        this.renderRows();
      });
  }

  /**
   * Clears any existing content in the header row outlet and creates a new embedded view
   * in the outlet using the header row definition.
   */
  private _forceRenderHeaderRows() {
    // Clear the header row outlet if any content exists.

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Pass a plain array: [dataSource]="myArray"
  2. Or pass an Observable: [dataSource]="data$"
  3. Or implement/extend MatTableDataSource (or DataSource<T>) and pass an instance
  4. Check the binding uses property binding [dataSource] not the literal attribute dataSource
  5. Convert a Promise with from(promise) or toDataSource-style wrapping

Example fix

// before
<table mat-table dataSource="users">
// after
<table mat-table [dataSource]="users">  <!-- users: User[] | Observable<User[]> | MatTableDataSource<User> -->
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidDataSource(src: unknown): boolean {
  return Array.isArray(src) || src instanceof Observable ||
    (typeof src === 'object' && src !== null && 'connect' in src);
}
if (!isValidDataSource(this.dataSource)) throw new TypeError('dataSource must be array, Observable, or DataSource');

Type guard

function isDataSource<T>(src: unknown): src is DataSource<T> {
  return typeof src === 'object' && src !== null && typeof (src as any).connect === 'function';
}

Try / catch

try {
  this.cdr.detectChanges();
} catch (e) {
  if (/did not match an array, Observable, or DataSource/.test(e.message)) {
    console.error('Use [dataSource] with an array, Observable, or MatTableDataSource instance');
  }
}

Prevention

When it happens

Trigger: In _observeRenderedContent / renderChanges: dataSource is truthy but not Array.isArray(dataSource), not a DataSource, and not a subscribed Observable, leaving dataStream === undefined.

Common situations: Passing a Promise directly (not supported); passing an incorrectly instantiated DataSource (e.g. missing connect()); binding the wrong variable such as dataSource="data" (string) instead of [dataSource]="data"; passing undefined via a wrong property path that isn't caught because of a truthy non-stream object.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/dcf99e1f7a0f07a0. Report an issue: GitHub.