OrchardCMS/OrchardCore · error · Error
Detected a connection attempt to an ASP.NET SignalR Server…
Error message
Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.
What it means
During negotiation the client checks negotiateResponse.ProtocolVersion; a presence of this field indicates the endpoint is a legacy ASP.NET (non-Core) SignalR server, whose protocol is incompatible with the @microsoft/signalr client. The client throws this descriptive error instead of failing later with cryptic protocol errors.
Solutions
- Migrate the server to ASP.NET Core SignalR (Microsoft.AspNetCore.SignalR) and use its hub endpoint.
- If the server must stay on ASP.NET SignalR 2.x, use the matching legacy client (jquery.signalR / Microsoft.AspNet.SignalR JS client).
- Verify the negotiate response: a Core server returns connectionId/availableTransports without ProtocolVersion.
- Check for reverse-proxy rewrites routing /signalr to a legacy host unintentionally.
Example fix
// before (legacy server + Core client)
const connection = new signalR.HubConnectionBuilder().withUrl("http://legacy-server/signalr").build();
// after (upgrade server to ASP.NET Core)
// Server: app.MapHub<MyHub>("/hub") with Microsoft.AspNetCore.SignalR
const connection = new signalR.HubConnectionBuilder().withUrl("https://core-app/hub").build(); Defensive patterns
Strategy: validation
Validate before calling
// client-side preflight
async function isAspNetCoreHub(url) {
const res = await fetch(`${url}/negotiate`, { method: "POST" });
const body = await res.json();
return body.ProtocolVersion === undefined; // Core servers omit ProtocolVersion
} Type guard
function isCoreNegotiateResponse(body) {
return body && body.connectionId !== undefined && body.ProtocolVersion === undefined;
} Try / catch
try {
await connection.start();
} catch (err) {
if (err.message.includes("ASP.NET SignalR Server")) {
console.error("Target is legacy ASP.NET SignalR — use matching client/server versions");
} else { throw err; }
} Prevention
- Verify server/client stack parity: @microsoft/signalr only speaks to ASP.NET Core SignalR.
- Check the negotiate payload during migration projects.
- Audit reverse-proxy routes so /signalr is not left pointing at a legacy host.
- Pin hub URLs per environment in configuration to avoid stale endpoints.
When it happens
Trigger: Pointing the JS client at an ASP.NET (Framework) SignalR endpoint /signalr/negotiate that returns a ProtocolVersion in its negotiate payload; mixing Microsoft.AspNet.SignalR server code with the @microsoft/signalr (Core) client.
Common situations: Migrating an old ASP.NET app to .NET Core and keeping the legacy hub URL; proxying '/signalr' to a legacy backend; upgrading the client package (@aspnet/signalr -> @microsoft/signalr) while the server is still ASP.NET SignalR 2.x.
Related errors
- Binary protocols over XmlHttpRequest not implementing…
- could not find global
- 'EventSource' is not supported in your environment.
- Expected a handshake response from the server.
- HttpConnection.stopConnection
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/7e4e056612d1ec15.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore.Modules/OrchardCore.SignalR/wwwroot/Scripts/signalr.js:2850
}
else {
throw new Error("Negotiation can only be skipped when using the WebSocket transport directly.");
}
}
else {
let negotiateResponse = null;
let redirects = 0;
do {
negotiateResponse = await this._getNegotiationResponse(url);
// the user tries to stop the connection when it is being started
if (this._connectionState === "Disconnecting" /* ConnectionState.Disconnecting */ || this._connectionState === "Disconnected" /* ConnectionState.Disconnected */) {
throw new AbortError("The connection was stopped during negotiation.");
}
if (negotiateResponse.error) {
throw new Error(negotiateResponse.error);
}
if (negotiateResponse.ProtocolVersion) {
throw new Error("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.");
}
if (negotiateResponse.url) {
url = negotiateResponse.url;
}
if (negotiateResponse.accessToken) {
// Replace the current access token factory with one that uses
// the returned access token
const accessToken = negotiateResponse.accessToken;
this._accessTokenFactory = () => accessToken;
// set the factory to undefined so the AccessTokenHttpClient won't retry with the same token, since we know it won't change until a connection restart
this._httpClient._accessToken = accessToken;
this._httpClient._accessTokenFactory = undefined;
}
redirects++;
} while (negotiateResponse.url && redirects < MAX_REDIRECTS);
if (redirects === MAX_REDIRECTS && negotiateResponse.url) {
throw new Error("Negotiate redirection limit exceeded.");
}View on GitHub (pinned to 4306c0717f)