dotnet/runtime · error · Error

No active JS diagnostic session

Error message

No active JS diagnostic session

What it means

Thrown by collectGcDump() when it is called with startup not set to true while the module-level serverSession is still undefined. serverSession is only assigned once a diagnostic client (dotnet-gcdump/dotnet-counters/dotnet-trace over the diagnostic server) has completed its advertise handshake. The guard prevents starting a GC heap-dump collection against a transport that has no peer to receive the commands and data.

Solutions

  1. If you intend to capture a heap dump at runtime startup before any client connects, call collectGcDump(options, true) so the startup branch bypasses the session check.
  2. Otherwise, first ensure a diagnostic client is connected (connectDSRouter or an external dotnet-gcdump session) so that serverSession is populated before invoking collectGcDump().
  3. Verify initializeDS() ran successfully during dotnetInitializeModule() in diagnostics/index.ts; if the diagnostic server failed to bind, serverSession will never be created.
  4. Check that DOTNET_DiagnosticPorts configuration allows the in-browser diagnostic server to accept a connection.

Example fix

// before
dotnetApi.collectGcDump({ durationSeconds: 5 });

// after - capture during startup, before a client connects
dotnetApi.collectGcDump({ durationSeconds: 5 }, /* startup */ true);
Defensive patterns

Strategy: validation

Validate before calling

// Before calling collectGcDump, confirm a diagnostic client is connected
// or pass startup=true to capture at boot.
import { serverSession } from "./diagnostic-server-js";

function canCollectGcDumpAtRuntime(): boolean {
  return !!serverSession; // false means you must pass startup=true
}

if (!canCollectGcDumpAtRuntime()) {
  console.warn("No diagnostic session; call collectGcDump(options, true) for a startup capture.");
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling dotnetApi.collectGcDump() (or the runtime invoking it via the diagnostics IPC) before any diagnostic client has connected, and without passing startup=true. The same pattern exists in collectCpuSamples() and collectMetrics() which also check `!startup && !serverSession`.

Common situations: App started dotnet with diagnostics enabled but no diagnostic tool attached yet; calling collectGcDump programmatically at app startup to capture an early heap snapshot; the diagnostic server failed to initialize (initializeDS() error swallowed) so serverSession never gets set.

Related errors


AI-assisted analysis of dotnet/runtime@60108ba66e (2026-08-10). Data as JSON: /api/errors/cd9b020248d61544. Report an issue: GitHub.

Appendix: source

Thrown at src/native/libs/System.Native.Browser/diagnostics/dotnet-gcdump.ts:14

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

import type { DiagnosticCommandOptions } from "../types";

import { commandResumeRuntime, commandStopTracing, commandGcHeapDump, } from "./client-commands";
import { dotnetLoaderExports, Module } from "./cross-module";
import { serverSession, setupJsClient } from "./diagnostic-server-js";
import { IDiagnosticSession } from "./types";

export function collectGcDump(options?: DiagnosticCommandOptions, startup?: boolean): Promise<Uint8Array[]> {
    if (!options) options = {};
    if (!startup && !serverSession) {
        throw new Error("No active JS diagnostic session");
    }

    const onClosePromise = dotnetLoaderExports.createPromiseCompletionSource<Uint8Array[]>();
    let stopDelayedAfterLastMessage = 0;
    let stopSent = false;
    function onSessionStart(session: IDiagnosticSession): void {
        session.sendCommand(commandResumeRuntime());
    }
    function onData(session: IDiagnosticSession, message: Uint8Array): void {
        session.store(message);
        if (!stopSent) {
            // stop durationSeconds (default 1s) after last GC message on this session, there will be more messages after that
            if (stopDelayedAfterLastMessage) {
                clearTimeout(stopDelayedAfterLastMessage);
            }
            stopDelayedAfterLastMessage = Module.safeSetTimeout(() => {
                stopSent = true;
                session.sendCommand(commandStopTracing(session.sessionId));

View on GitHub (pinned to 60108ba66e)