Skip to content

fix(#489): make JDI bridge pause idempotent on an already-suspended VM - #490

Merged
debugmcpdev merged 1 commit into
mainfrom
fix/java-attach-double-suspend
Aug 26, 2026
Merged

fix(#489): make JDI bridge pause idempotent on an already-suspended VM#490
debugmcpdev merged 1 commit into
mainfrom
fix/java-attach-double-suspend

Conversation

@debugmcpdev

Copy link
Copy Markdown
Collaborator

Summary

Fixes #489 — a regression from #483: attaching to a JVM launched with JDWP suspend=y left the target permanently frozen.

A suspend=y JVM already holds every thread at JDI suspend count 1. The #465 post-attach pause called vm.suspend() unconditionally, raising the count to 2, while continue's single vm.resume() only took it back to 1 — so the first continue "succeeded" but nothing ever ran: breakpoints never fired, waitForPausedState timed out, and 4 e2e tests failed (mcp-server-smoke-java-attach tests 1–2, mcp-server-smoke-java-redefine both). #483 was verified live only against suspend=n, and these stdio e2e smokes don't run in CI, so it landed green.

Fix

handlePause in the JDI bridge is now idempotent:

  • Pause-all branch: skip vm.suspend() when every thread is already suspended (logged: "Pause: VM already fully suspended; reporting stopped without deepening suspend count"), but still emit the stopped event — that resolves the session layer's post-attach stoppedSeen wait and keeps the client state machine settled.
  • Single-thread branch: t.suspend() only when the thread isn't already suspended.

Pausing a genuinely running VM is unchanged — its threads are unsuspended, so the real suspend path executes; the #352 e2e test guards this.

Also a comment-only note on JavaAdapterPolicy.getAttachBehavior documenting the idempotency contract.

Testing

No new unit test: the bridge has no JVM-free test harness and the behavior depends on live JDI suspend-count state. The regression proof is the four previously-failing e2e tests, which encode the exact double-suspend sequence:

✓ tests/e2e/mcp-server-smoke-java-attach.test.ts (3 tests) — incl. #352 counter-case
✓ tests/e2e/mcp-server-smoke-java-redefine.test.ts (2 tests)

Log verification: [SessionManager] Sent post-attach pause (threadId=0) fires for both attach tests, and the No 'stopped' event within …ms warning no longer appears.

🤖 Generated with Claude Code

A JVM launched with JDWP suspend=y already holds every thread at JDI
suspend count 1. The #465 post-attach pause called vm.suspend()
unconditionally, raising the count to 2, while continue's single
vm.resume() only took it back to 1 - so the first continue succeeded
but the VM never ran again: breakpoints never fired and the session was
permanently stuck. Regression from #483, which was only verified live
against suspend=n.

handlePause now skips vm.suspend() when every thread is already
suspended (and t.suspend() for an already-suspended single thread) but
still emits the stopped event so the session layer's post-attach
stoppedSeen wait resolves and the client state machine settles. Pausing
a genuinely running VM is unchanged, which the #352 e2e test guards.

No new unit test: the bridge has no JVM-free test harness and the
behavior depends on live JDI suspend-count state. The four previously
failing e2e tests (mcp-server-smoke-java-attach tests 1-2,
mcp-server-smoke-java-redefine both) encode the exact double-suspend
sequence and now pass, alongside the #352 counter-case.

Fixes #489

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@debugmcpdev
debugmcpdev merged commit 9c7179c into main Aug 26, 2026
10 checks passed
@debugmcpdev
debugmcpdev deleted the fix/java-attach-double-suspend branch August 26, 2026 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression from #483: java attach to a suspend=y JVM double-suspends - first continue never resumes the target

2 participants