Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1135.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**`get_local_variables` and `evaluate_expression` say when the frame they answer from is not your program's.** On a stop where no frame is program code — a pause on an idle Node program, which lands in the timer loop; a `step_out` of `main` in C or C++, which lands in the C runtime — `get_stack_trace` and the pause and step answers said `noProgramFrame` and "No program frame is active…", while `get_local_variables` returned the runtime frame's variables (`now = 4210`, `[return value] = 0`) as plain locals, and `evaluate_expression` without a `frameId` ran there without a word. Both now carry `noProgramFrame: true` and the same sentence, with one clause of their own: "The variables shown belong to '<frame>', not to your program." and "The expression was evaluated in that frame." The variables are still returned — they are what the adapter reported — and an explicit `frameId` adds nothing, since the caller chose the frame. A Rust `step_out` of `main` is not covered yet: its landing frame is not classified as runtime (#1131). (#1135)
1 change: 1 addition & 0 deletions changelog.d/1139.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**A Python or Ruby attach to a port that does not speak DAP now says so, instead of describing an adapter that never existed.** These attaches start nothing: the server connects straight to the host and port given, so "the adapter" is whatever listens there. Attaching to a listener that accepts the connection and stays silent — the wrong port — waited 30 s and answered "…an adapter-side protocol stall, not a missing install — the adapter started and accepted the connection. Retrying usually succeeds", and retrying stalled identically. Attaching to a real HTTP port (the application's own, say), which hangs up on the DAP frame, answered "Critical initialization error: DAP client disconnected [Executable: python | adapter PID=none exitCode=n/a]", and a port that resets the connection (a forwarder with nothing behind it) answered the bare "DAP client error: read ECONNRESET". All three now name the endpoint and say that nothing was started for the attach; the stall, for one, reads: "127.0.0.1:5000 accepted the connection but never answered the DAP "initialize" request. Nothing was started for this attach: the server connected to what is already listening on that port, so check that it is a debugpy listener (python -m debugpy --listen 127.0.0.1:5000 …) and not another service. Retrying the same port stalls the same way." Once the endpoint has answered `initialize` it is a debug server, and a later stall is worded as its own; if it then closes or resets the connection mid-handshake, the answer is "Critical initialization error: DAP client disconnected [direct connect to host:port; no adapter process was started]" where it used to be "Proxy exited during initialization…" with lines of worker stderr, or "Error in DAP sequence: DAP client disconnected" (a server that first ends the session with a DAP `terminated` event is still answered as a proxy exit: #1152); a connection that was never made no longer says to check that the adapter is installed. The answer for a stall (the 30 s deadline) gains `data.initProgress.directConnect` (`host`, `port`, `listener`). The spawned-adapter messages are unchanged, and so is the 30 s deadline. (#1139)
1 change: 1 addition & 0 deletions changelog.d/1140.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**`snapshot.changedVariables` says when a variable is new or gone, instead of inventing a value for it.** A variable that came into scope between two stops was listed as a change from the string `"<undefined>"` — JavaScript's vocabulary for a Java, Rust, Dart or Python local that simply did not exist yet, indistinguishable from a real value, and for js-debug's `Return value` a "change" from `<undefined>` to `undefined`. A variable that left scope silently dropped out, so "unchanged" and "gone" looked the same. Each entry now states which of three things happened, and the side that does not exist is left out: `{ "before": "1", "after": "2" }` for a changed value, `{ "after": "30", "new": true }` for a variable that appeared, `{ "before": "3", "gone": true }` for one that left scope. An empty `{}` still means nothing visible changed. The flags are only set when they are known: the locals a snapshot shows are capped at 20, so a name missing from a list that was cut may only not have fitted — a variable absent from a cut previous list is `{ "after": … }` with no `new`, and one absent from a cut current list is not listed. **Changed shape:** an entry for a new variable no longer has `before`; a client that read `before: "<undefined>"` should read `new: true`. (#1140)
1 change: 1 addition & 0 deletions changelog.d/1141.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**`get_stack_trace` no longer sends a caller round in a circle, and an ended session is explained by every inspection tool.** Two guidance gaps. On a stop where no frame is program code, `get_stack_trace` ended its note with "get_stack_trace with includeInternals: true lists the frames" — including for a caller who had just passed `includeInternals: true` and was looking at every frame. That clause is now added only by an answer that has not shown the thread's whole stack (a pause or step answer on a thread with more than one frame), and never by `get_stack_trace` itself. And after a program ran to completion, `get_stack_trace`, `get_scopes`, `get_variables` and `evaluate_expression` answered a bare `Session is terminated: <id>` where `get_local_variables` explained the state. All five now carry the same `message`, worded for what the session is: a launched program is run again with `restart_debugging`; an attach session, which `restart_debugging` refuses, is attached again with `attach_to_process` (it used to be told "Use restart_debugging" as well); a child session names its parent. The `error` text is unchanged. (#1141)
1 change: 1 addition & 0 deletions changelog.d/1142.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**A `set_breakpoint` refusal no longer suggests a `statement` that cannot be found.** When `expectedContent` did not match the line, the refusal ended "Or address by content: set_breakpoint {statement: "<the expected text>"}" whatever that text was — including text that is nowhere in the file, where following the hint answered `statement … not found`. The sentence is now the statement resolver's own answer for the expected text, so the two cannot disagree: on one line it names it ("…that text is on line 11."); on several it adds `nearLine` and lists the lines; and when the resolver would not find it — the text is absent, or it is only in a comment or past a `//` or `#`, where statement matching does not look — no statement is offered and the refusal names the lines the same `expectedContent` would pass on ("That expectedContent matches line 12."), or says "That expectedContent matches no line of the file." An expectation that spans lines gets no such sentence. (#1142)
6 changes: 3 additions & 3 deletions docs/architecture/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ Steps into a function call.
#### `stepOut(sessionId: string): Promise<DebugResult<StepResultData>>`
Steps out of the current function.

The three step methods take only a `sessionId` and return a typed result, not `void`. `StepResultData` adds a required `message` plus the `StopAnswerFields` every stop answer shares (issue #904: an optional `location` — `{ file, line, column? }`, the first visible frame as the frame-anchor resolver reads it through `describeStopLocation` in `src/session/inspection/stop-location.ts` — and `threadId`/`note` only when the answer is anchored off the reported thread or no frame of the shown stack is program code; plus the stop `snapshot`, issues #982/#983 — composed once per answer after any same-line repeat, with `changedVariables` in place of `locals` on a repeated stop in the same frame, keyed off the session's `snapshotDiffBase`) and, when the stop that ended the step was recorded as something other than the step, `stopReason`/`rawStopReason` with the same semantics as `PauseResultData` (issue #678). If no `stopped` event arrives before the step grace window elapses, the call still succeeds with `data.pending = true` — that window is a reporting deadline, not a deadline on the debuggee.
The three step methods take only a `sessionId` and return a typed result, not `void`. `StepResultData` adds a required `message` plus the `StopAnswerFields` every stop answer shares (issue #904: an optional `location` — `{ file, line, column? }`, the first visible frame as the frame-anchor resolver reads it through `describeStopLocation` in `src/session/inspection/stop-location.ts` — and `threadId`/`note` only when the answer is anchored off the reported thread or no frame of the shown stack is program code; plus the stop `snapshot`, issues #982/#983 — composed once per answer after any same-line repeat, with `changedVariables` in place of `locals` on a repeated stop in the same frame, keyed off the session's `snapshotDiffBase` — each entry a `ChangedVariable`: `{ before, after }`, `{ after, new: true }` or `{ before, gone: true }`, issue #1140) and, when the stop that ended the step was recorded as something other than the step, `stopReason`/`rawStopReason` with the same semantics as `PauseResultData` (issue #678). If no `stopped` event arrives before the step grace window elapses, the call still succeeds with `data.pending = true` — that window is a reporting deadline, not a deadline on the debuggee.

#### `pause(sessionId: string, threadId?: number): Promise<DebugResult<PauseResultData>>`
Pauses execution. `PauseResultData` adds `message`, optional `stopReason`/`rawStopReason` (present only when this pause's own stop was recorded, never a stale earlier one) and the `StopAnswerFields` (`location`, `threadId`, `note`, `snapshot` — issue #982; the "Already paused" fast path carries none of them). A pause is read with the resolver's readiness option, so one reported on a thread with no program frame is anchored on the first sibling whose stack reaches the program (issue #898), and one where no thread has a program frame says so in `note` (issue #900).
Expand Down Expand Up @@ -393,8 +393,8 @@ Closes the mirror endpoint via the `mirrorUnexpose` pseudo-command and disconnec
#### `listThreads(sessionId: string): Promise<Array<{ id: number; name: string }>>`
Lists all threads in the debug session.

#### `getLocalVariables(sessionId: string, includeSpecial?: boolean, names?: string[]): Promise<{ variables: Variable[]; frame: { name: string; file: string; line: number } | null; scopeName: string | null; anchorNote?: string; truncation?: VariableTruncationSummary }>`
Orchestrates stack trace → scopes → variables, then delegates to the language adapter's policy to extract just the locals. If `includeSpecial` is false (default), internal/special variables are filtered out; `names` is an optional exact-match, case-sensitive filter applied to the extracted locals. The return type is the exported `LocalVariablesResult` (issue #982 named it; also what the protected `extractLocalsFromResolved` — the same pipeline behind an already-resolved stack, which the stop snapshot uses — returns). `anchorNote` explains any departure from "the top frame's local scope" (a lower frame was anchored, and/or a sibling scope supplied the variables); `truncation` appears when the size guards cut the response.
#### `getLocalVariables(sessionId: string, includeSpecial?: boolean, names?: string[]): Promise<{ variables: Variable[]; frame: { name: string; file: string; line: number } | null; scopeName: string | null; anchorNote?: string; noProgramFrame?: NoProgramFrameVerdict; truncation?: VariableTruncationSummary }>`
Orchestrates stack trace → scopes → variables, then delegates to the language adapter's policy to extract just the locals. If `includeSpecial` is false (default), internal/special variables are filtered out; `names` is an optional exact-match, case-sensitive filter applied to the extracted locals. The return type is the exported `LocalVariablesResult` (issue #982 named it; also what the protected `extractLocalsFromResolved` — the same pipeline behind an already-resolved stack, which the stop snapshot uses — returns). `anchorNote` explains any departure from "the top frame's local scope" (a lower frame was anchored, and/or a sibling scope supplied the variables); `noProgramFrame` (`{ threadId?, frameCount, top }`, issue #1135) is the resolver's verdict that no frame of the stack is program code — the variables are then a runtime frame's, and the tool answer says so; `truncation` appears when the size guards cut the response.

### Session Management

Expand Down
5 changes: 4 additions & 1 deletion docs/javascript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,10 @@ open on its port (Node never closes it by itself) — a later attach by PID or b
freezes on its next request; or `paused` at once on a sourceless `<anonymous>`
frame between dispatches, with `note` saying no program frame is active and
`get_local_variables` empty (issue #900) — continue and send a request, or set a
breakpoint. Attach to a live server you still need to use with `stopOnEntry: false`
breakpoint. A pause that lands in a named runtime frame instead (a launched
program idle between timers stops in `process.processTimers`) has variables of
its own: `get_local_variables` returns them with `noProgramFrame: true` and a
`note` saying they belong to that frame, not to your program (issue #1135). Attach to a live server you still need to use with `stopOnEntry: false`
- Some advanced DAP features may not be exposed through MCP tools
- **Source-mapped frames you cannot open.** A package that ships `.js.map` files
whose `sources` point at `.ts` files it did not ship makes js-debug report those
Expand Down
Loading
Loading