dotnet/aspnetcore · critical · Error
No usable HttpClient found.
Error message
No usable HttpClient found.
What it means
DefaultHttpClient picks an HTTP backend at construction time: it prefers the Fetch API (or Node's fetch/Platform.isNode), then falls back to XMLHttpRequest. If neither global is present, there is no transport available to perform HTTP requests, so the constructor throws synchronously.
Solutions
- Upgrade to Node.js 18+ where fetch is a global, or install and register a fetch polyfill (e.g. node-fetch assigned to globalThis.fetch) before constructing the client.
- If you must support an older environment, supply your own HttpClient implementation to HttpConnection via options.httpClient.
- Verify that the bundler is not stripping window.fetch/XMLHttpRequest via dead-code elimination; mark the SignalR package as external or add the globals to your platform.
- In React Native, ensure the networking globals are loaded before any SignalR import.
Example fix
// before: Node 16, no fetch global
const connection = new signalR.HubConnectionBuilder().withUrl(url).build();
// after: polyfill fetch before importing signalr
import { fetch as undiciFetch } from "undici";
(globalThis as any).fetch = (globalThis as any).fetch ?? undiciFetch;
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder().withUrl(url).build(); Defensive patterns
Strategy: validation
Validate before calling
function hasHttpBackend(): boolean {
return typeof fetch !== "undefined" || typeof XMLHttpRequest !== "undefined";
}
if (!hasHttpBackend()) {
// register polyfills before importing signalr
throw new Error("Environment has no HTTP backend — install node-fetch or upgrade Node.");
} Type guard
function supportsDefaultHttpClient(): boolean {
return typeof fetch !== "undefined" || typeof XMLHttpRequest !== "undefined" || (typeof process !== "undefined" && process.versions?.node != null && typeof globalThis.fetch === "function");
} Try / catch
try {
const client = new signalR.HttpClient(...);
} catch (e) {
if (e instanceof Error && e.message.includes("No usable HttpClient")) {
// install polyfill and retry, or supply a custom HttpClient
}
throw e;
} Prevention
- Run on Node 18+ (built-in fetch) or polyfill globalThis.fetch before importing signalr.
- Verify fetch/XMLHttpRequest exist in your target runtime as part of your smoke tests.
- For exotic runtimes, implement and inject a custom HttpClient via options.httpClient.
When it happens
Trigger: Instantiating new DefaultHttpClient(logger) in a JavaScript runtime that exposes neither global fetch nor global XMLHttpRequest. Common in older Node.js (< 18, no built-in fetch) without a polyfill, or in a minimal/embedded JS engine (QuickJS, duktape, some React Native renderers without the network polyfill).
Common situations: Running the SignalR client on Node.js 16 or earlier (fetch is not global there). Bundling for a non-browser target with tree-shaking that strips the fetch polyfill. Using the client inside a Web Worker context that lacks XMLHttpRequest in some browsers, when fetch detection fails.
Related errors
- Cannot resolve ' '.
- 'EventSource' is not supported in your environment.
- 'WebSocket' is not supported in your environment.
- could not find global
- No method defined.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/e93e6b01da6595d1.
Report an issue: GitHub.
Appendix: source
Thrown at src/SignalR/clients/ts/signalr/src/DefaultHttpClient.ts:24
import { HttpClient, HttpRequest, HttpResponse } from "./HttpClient";
import { ILogger } from "./ILogger";
import { Platform } from "./Utils";
import { XhrHttpClient } from "./XhrHttpClient";
/** Default implementation of {@link @microsoft/signalr.HttpClient}. */
export class DefaultHttpClient extends HttpClient {
private readonly _httpClient: HttpClient;
/** Creates a new instance of the {@link @microsoft/signalr.DefaultHttpClient}, using the provided {@link @microsoft/signalr.ILogger} to log messages. */
public constructor(logger: ILogger) {
super();
if (typeof fetch !== "undefined" || Platform.isNode) {
this._httpClient = new FetchHttpClient(logger);
} else if (typeof XMLHttpRequest !== "undefined") {
this._httpClient = new XhrHttpClient(logger);
} else {
throw new Error("No usable HttpClient found.");
}
}
/** @inheritDoc */
public send(request: HttpRequest): Promise<HttpResponse> {
// Check that abort was not signaled before calling send
if (request.abortSignal && request.abortSignal.aborted) {
return Promise.reject(new AbortError());
}
if (!request.method) {
return Promise.reject(new Error("No method defined."));
}
if (!request.url) {
return Promise.reject(new Error("No url defined."));
}
return this._httpClient.send(request);View on GitHub (pinned to 3600ca084e)