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
- Pass a plain array: [dataSource]="myArray"
- Or pass an Observable: [dataSource]="data$"
- Or implement/extend MatTableDataSource (or DataSource<T>) and pass an instance
- Check the binding uses property binding [dataSource] not the literal attribute dataSource
- 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
- Always use property binding [dataSource]="..."
- Convert Promises with from(promise) before assigning
- Use MatTableDataSource for filtering/sorting/paging features
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
- Conditional row definitions via the `when` input are not sup
- Specified anchor does not implement the `getAnchor` method.
- Missing definitions for header, footer, and row; cannot dete
- Duplicate column definition name provided: "${columnDef.name
- There can only be one default row without a when predicate f
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/dcf99e1f7a0f07a0.
Report an issue: GitHub.