Debugging Overview

Purpose and Scope

Debugging in VS Code is the workflow for starting a program under a debugger, pausing execution at breakpoints, inspecting state, stepping through code, and using diagnostics to understand what went wrong. The first-party documentation describes the visible experience around the Run and Debug view, debug toolbar, debug console, call stack, breakpoints, variables, watch expressions, and Run menu. This page orients developers and contributors to that experience, then maps the supplied repository evidence to the automated diagnostic and Copilot-assisted debugging surfaces that help make the workflow reliable.

VS Code treats debugging as a common user interface over many runtime-specific debuggers. JavaScript, TypeScript, and Node.js debugging are built in, while other runtimes typically arrive through debugger extensions and the Debug Adapter Protocol. That separation matters when reading repository tests: a failure might belong to the UI, a debugger extension, a language service diagnostic provider, a chat tool that gathers errors, or telemetry that records an AI-assisted session. The evidence here emphasizes the last three areas: diagnostics, Copilot debugging support, and error handling around context retrieval.

Sources: extensions/copilot/test/simulation/diagnosticProviders/index.ts, extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getDiagnostics.spec.ts

Debugging Concepts in VS Code

A debugging session starts when the user selects a launch configuration, asks VS Code to debug the active file, or invokes a debug command contributed by an extension. The launch configuration, commonly stored in launch.json, describes enough about the target runtime for VS Code and the debugger extension to start, attach, pass arguments, and connect to the right process. Once active, the debugger surfaces state in a consistent workbench layout: the debug toolbar controls continue, pause, step over, step into, step out, restart, and stop; the debug console accepts evaluation; and the sidebar exposes variables, watches, call stacks, and breakpoints.

The official debugging model is intentionally language-neutral. Extension authors integrate real debuggers by adapting runtime-specific behavior to the Debug Adapter Protocol, which lets the workbench reuse the same breakpoint, stack, variable, and console UI across languages. From a contributor perspective, this means debugger features are rarely isolated to a single file. A high-quality debugging flow depends on launch configuration management, language diagnostics, UI state, extension activation, debug adapter communication, and tests that prove failures are reported in ways the user or an AI assistant can act on.

In modern VS Code, debugging also includes AI-assisted setup and investigation. Copilot can help generate launch configurations, interpret diagnostics, and use workspace context to suggest repairs. That assistance still depends on ordinary editor facts: diagnostics must be collected, severity must be normalized, file URIs must be preserved, and failures in search or indexing must be classified. The repository tests in this page show those contracts at their boundaries, especially where Copilot tools observe vscode.languages.getDiagnostics, listen for diagnostic changes, and serialize results for a tool caller.

Sources: extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/diagnosticsChanged.spec.ts, extensions/copilot/src/extension/completions-core/vscode-node/lib/src/prompt/test/defaultDiagnosticSettings.test.ts

Relevant Source Files

  • extensions/copilot/src/platform/remoteSearch/test/node/codeOrDocsSearchErrors.spec.ts - verifies classification of code and documentation search failures that can affect AI-assisted debugging when repository context is inaccessible, unindexed, unavailable, or blocked.
  • extensions/copilot/test/simulation/diagnosticProviders/index.ts - registers the known diagnostic providers used in Copilot simulations, including TypeScript, ESLint, Pyright, Pylint, Roslyn, C++, Ruff, and an import-error-aware TypeScript variant.
  • extensions/copilot/src/extension/chat/vscode-node/test/chatDebugFileLoggerService.spec.ts - tests the chat debug file logger service with OpenTelemetry-like completed spans and span events, including chat and tool-call span attributes.
  • extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/diagnosticsChanged.spec.ts - tests push notification behavior for diagnostics changes, including registration, debounce behavior, severity mapping, URI payloads, and notification broadcast through an in-process HTTP server boundary.
  • extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getDiagnostics.spec.ts - tests the get_diagnostics tool registration and result shape for URI-scoped and workspace-wide diagnostic queries.
  • extensions/copilot/src/extension/completions-core/vscode-node/lib/src/prompt/test/defaultDiagnosticSettings.test.ts - tests parsing and defaults for DefaultDiagnosticSettings, including warning policy, diagnostic limits, and line-distance behavior used when diagnostics are included in completion context.

System-to-Code Mapping

The user-facing debugger experience starts with interactive commands and views, but the supplied source evidence maps most directly to diagnostics as debugging input. KnownDiagnosticProviders centralizes simulated providers for TypeScript, ESLint, Python, .NET, C++, and Ruff-backed workflows, then exposes getDiagnostics(accessor, files, providerId) as a uniform test helper. That contract mirrors the debugging reality that VS Code may receive signals from many language ecosystems before the user even starts a debug session. Problems discovered by a language service often guide breakpoint placement, launch configuration fixes, or the first Copilot question a user asks.

Sources: extensions/copilot/test/simulation/diagnosticProviders/index.ts

The Copilot CLI session tests expose diagnostics through two complementary mechanisms. The get_diagnostics tool is request-oriented: it registers a tool named get_diagnostics, accepts an optional file URI, calls VS Code diagnostics APIs, and returns serialized file results containing URI, file path, message, severity, source, code, and range information. The diagnostics-changed notification is event-oriented: it listens for diagnostic changes, debounces rapid events, fetches current diagnostics, and broadcasts a diagnostics_changed notification. Together they model both pull and push access to debugging context.

Sources: extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getDiagnostics.spec.ts, extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/diagnosticsChanged.spec.ts

Diagnostic selection also appears in completion prompt configuration. DefaultDiagnosticSettings.from() accepts JSON input and rejects null, undefined, empty strings, invalid JSON, and empty objects by returning undefined. When valid data is present, tests show fields such as warnings, maxLineDistance, and maxDiagnostics, with defaults for invalid or omitted values. This matters for debugging-adjacent AI help because a completion or edit suggestion should not blindly include every warning in a workspace. The settings bound how much problem context is eligible and how close it must be to the user’s active code.

Sources: extensions/copilot/src/extension/completions-core/vscode-node/lib/src/prompt/test/defaultDiagnosticSettings.test.ts

Execution Flow for Diagnostic-Assisted Debugging

A practical debugging flow often starts before pressing the Run button. The user opens a file, sees squiggles or Problems entries, and decides whether the issue is a compile-time diagnostic, a runtime exception, or a launch setup problem. In the Copilot-assisted path represented here, a tool can first query diagnostics for the active URI. If no URI is supplied, tests cover returning diagnostics across files. Severity normalization to strings such as error, warning, information, and hint gives downstream agents a stable vocabulary instead of leaking VS Code enum internals.

Sources: extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getDiagnostics.spec.ts

When diagnostics change while a user edits, the event path keeps external or agentic participants current. The diagnostics-changed tests mock languages.onDidChangeDiagnostics, capture the registered callback, and use fake timers to prove that rapid changes are debounced before broadcast. That debounce is an important debugging usability constraint: a tool consumer should learn that the problem set changed, but it should not be flooded with every transient edit state while the user is typing. The test also covers cleared diagnostics, which is the positive debugging signal that a fix removed the reported problem.

Sources: extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/diagnosticsChanged.spec.ts

If the debugging question requires broader code or documentation context, search failures must be understandable. The search-error tests construct repository-level and endpoint-level failures and assert specific error classes for inaccessible repository organizations, unavailable embeddings, not-indexed repositories, exceeded retry limits, and missing endpoint access. For AI-assisted debugging, those distinctions are more useful than a generic failure. They let the product explain whether the workspace cannot be searched, the remote service is not ready, access is missing, or the request exhausted retries.

Sources: extensions/copilot/src/platform/remoteSearch/test/node/codeOrDocsSearchErrors.spec.ts

API Components and Contracts

The clearest public-like contract in the supplied evidence is the MCP-style diagnostic tool. The test suite expects a tool named get_diagnostics to be registered and callable with either { uri: 'file:///test/file.ts' } or an empty object. A URI-scoped result is an array of file result objects; each file result includes uri, filePath, and diagnostics. Each diagnostic includes message, normalized severity, optional source, optional code, and a range with start and end line and character positions. Consumers should treat an empty array as a valid no-problems result, not as a transport error.

// Conceptual result shape exercised by getDiagnostics.spec.ts
type DiagnosticsFileResult = {
  uri: string;
  filePath: string;
  diagnostics: Array<{
    message: string;
    severity: 'error' | 'warning' | 'information' | 'hint' | string;
    source?: string;
    code?: string | number;
    range: {
      start: { line: number; character: number };
      end: { line: number; character: number };
    };
  }>;
};

Sources: extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getDiagnostics.spec.ts

The push contract is diagnostics_changed. Its payload groups changed URIs with their current diagnostics, including the same message, severity, source, code, and range information. Tests verify listener registration, notification broadcast, and debouncing, so implementers should assume a short delay between editor changes and notification delivery. This delay is not a debugger pause; it is an event-coalescing behavior designed to keep diagnostic consumers stable. A consumer that needs immediate state should call the request-style get_diagnostics tool after receiving the push notification.

Sources: extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/diagnosticsChanged.spec.ts

The diagnostic prompt settings contract is deliberately tolerant. DefaultDiagnosticSettings.from() returns undefined when the setting is absent or unusable, parses valid JSON, and normalizes unsupported warnings values to no. Supported warning behavior in tests includes yes, no, and yesIfNoErrors. Numeric settings such as maxLineDistance and maxDiagnostics have defensive defaults when omitted or invalid. This design keeps debugging assistance predictable: malformed configuration should reduce diagnostic context rather than crash completions, and warning inclusion should remain explicit.

Sources: extensions/copilot/src/extension/completions-core/vscode-node/lib/src/prompt/test/defaultDiagnosticSettings.test.ts

Logging, Telemetry, and Failure Signals

Debugging an AI-assisted debugging feature requires its own observability. The chat debug file logger tests construct completed spans with operation names for chat and tool execution, request model attributes, token usage, and chat session identifiers. They also provide a test OpenTelemetry service with events for completed spans and span events. Even without exposing the implementation body here, the test setup shows the service boundary: chat debug logging reacts to telemetry-style span completion and records enough structure to correlate tool calls, chat requests, model choices, token counts, and sessions during investigation.

Sources: extensions/copilot/src/extension/chat/vscode-node/test/chatDebugFileLoggerService.spec.ts

Those logging signals complement the search-error classifications. If Copilot cannot answer a debugging question because code search failed, the error class should explain the category. If a diagnostic tool returned no results, that may be a clean file rather than failure. If a diagnostics notification was delayed, debounce behavior may be expected. The debugging overview for contributors should therefore include both user-visible concepts and machine-visible evidence. Breakpoints and step actions show what the user is doing; diagnostics, telemetry spans, and typed errors show what the product and assistant know about the session.

Sources: extensions/copilot/src/platform/remoteSearch/test/node/codeOrDocsSearchErrors.spec.ts, extensions/copilot/src/extension/chat/vscode-node/test/chatDebugFileLoggerService.spec.ts

Testing Signals

The tests use Vitest-style describe, it, expect, and mocks for Copilot chat-session components, while the default diagnostic settings test uses the older suite/test pattern with Node assertions. That mixture reflects VS Code’s broad test surface: workbench, extensions, and Copilot code can have different test harnesses while still verifying user-facing contracts. For debugging-related changes, the important signal is not the framework name but the behavior under test: registration occurs, payloads preserve file and range data, severity mapping is stable, invalid configuration is safe, and known remote-search failures are represented by specific error types.

Sources: extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/getDiagnostics.spec.ts, extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/diagnosticsChanged.spec.ts, extensions/copilot/src/extension/completions-core/vscode-node/lib/src/prompt/test/defaultDiagnosticSettings.test.ts, extensions/copilot/src/platform/remoteSearch/test/node/codeOrDocsSearchErrors.spec.ts

When extending debugging or AI diagnostic behavior, start by deciding which layer owns the change. A launch configuration or adapter change belongs to the debugger integration path. A Problems or language-service change belongs to diagnostics. A Copilot investigation feature should usually consume diagnostics through a stable tool or provider boundary rather than duplicating language-specific logic. After the implementation, add tests that cover empty states, cleared diagnostics, invalid configuration, severity conversion, URI preservation, and failures in remote context lookup. Next, read Launch Configurations for setup details and Testing Experience for repository test structure.