angular/angular-cli · error · SynchronousDelegateExpectedException
Expected a synchronous delegate but got an asynchronous one.
Error message
Expected a synchronous delegate but got an asynchronous one.
What it means
SyncDelegateHost wraps a Host and guarantees every operation completes synchronously. In the constructor it checks the delegate host's capabilities.synchronous flag and throws SynchronousDelegateExpectedException if the underlying host can only return Observables that may resolve asynchronously. This protects callers who expect plain return values rather than Observables.
Source
Thrown at packages/angular_devkit/core/src/virtual-fs/host/sync.ts:34
HostCapabilities,
HostWatchEvent,
HostWatchOptions,
Stats,
} from './interface';
export class SynchronousDelegateExpectedException extends BaseException {
constructor() {
super(`Expected a synchronous delegate but got an asynchronous one.`);
}
}
/**
* Implement a synchronous-only host interface (remove the Observable parts).
*/
export class SyncDelegateHost<T extends object = {}> {
constructor(protected _delegate: Host<T>) {
if (!_delegate.capabilities.synchronous) {
throw new SynchronousDelegateExpectedException();
}
}
protected _doSyncCall<ResultT>(observable: Observable<ResultT>): ResultT {
let completed = false;
let result: ResultT | undefined = undefined;
let errorResult: Error | undefined = undefined;
// Perf note: this is not using an observer object to avoid a performance penalty in RxJS.
// See https://github.com/ReactiveX/rxjs/pull/5646 for details.
observable.subscribe(
(x: ResultT) => (result = x),
(err: Error) => (errorResult = err),
() => (completed = true),
);
if (errorResult !== undefined) {
throw errorResult;
}View on GitHub (pinned to bb72145f9a)
Solutions
- Use a host with capabilities.synchronous === true (e.g. CordHost backed by a sync host, or a host explicitly implementing the sync capability)
- Implement capabilities.synchronous: true on your custom Host, ensuring all its Observable-returning methods actually emit synchronously (of(), not from(promise))
- Use the async host directly and subscribe to the Observables instead of wrapping it in SyncDelegateHost
Example fix
// before const host = new SyncDelegateHost(new SimpleMemoryHost()); // after const syncHost = new SimpleMemoryHost(); syncHost.capabilities.synchronous = true; // only if reads are truly sync const host = new SyncDelegateHost(syncHost);
Defensive patterns
Strategy: validation
Validate before calling
import { SyncDelegateHost } from '@angular-devkit/core/src/virtual-fs/host/sync';
function assertSyncHost(host) {
if (!host.capabilities || host.capabilities.synchronous !== true) {
throw new Error('Host must report capabilities.synchronous === true');
}
return host;
}
const host = new SyncDelegateHost(assertSyncHost(delegate)); Type guard
function isSyncCapable(host) {
return typeof host === 'object' && host !== null &&
host.capabilities?.synchronous === true;
} Try / catch
try {
const host = new SyncDelegateHost(delegate);
} catch (e) {
if (e.constructor.name === 'SynchronousDelegateExpectedException') {
// fall back to async Host API
} else { throw e; }
} Prevention
- Check capabilities.synchronous before wrapping any host in SyncDelegateHost
- Prefer using the async Host API directly in new code (SyncDelegateHost is deprecated)
- For custom hosts, only set synchronous: true if every method emits synchronously
When it happens
Trigger: Constructing new SyncDelegateHost(delegate) with a host whose capabilities.synchronous is false or undefined — e.g. wrapping a plain Host<T> or SimpleMemoryHost instead of a sync-capable host.
Common situations: Passing the default async filesystem host to tooling that expects sync access (older Angular CLI schematics/utilities that used SyncDelegateHost), or forgetting to wrap a host in a sync adapter that reports synchronous: true.
Related errors
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/0e0ac70a75bd39f5.
Report an issue: GitHub.