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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@
{
"name": "debug_java_application",
"displayName": "Debug Java Application",
"modelDescription": "Launch or attach to a Java application in debug mode with automatic compilation and classpath resolution. The tool handles building the project, resolving dependencies, starting the JVM with JDWP enabled, and auto-attaching the VS Code debugger. Use this as the first step to establish a debug session. The debug process runs in the background until stopped. Example usage: Debug a main class ('com.example.Main'), a JAR file ('target/app.jar'), or with program arguments (['--port=8080']).",
"modelDescription": "Build and debug Java with automatic classpath resolution. On failure/timeout, diagnose; no automatic retry or terminal relaunch. Timeout is unconfirmed startup: check status once, do not stop the launch. Retry only after fixing the cause or explicit user request; check existing sessions first.",
"toolReferenceName": "debugJavaApplication",
"tags": [
"java",
Expand Down
6 changes: 5 additions & 1 deletion resources/instruments/javaDebugContext.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ For Java run/launch/debug/inspection requests, prefer the Java debug language mo

If both apply (e.g. "launch and break on entry of `Main.foo`"), load `java-launch-troubleshooting` first, then `java-debug-inspection` after the session is active.

Fall back to `run_in_terminal` only when `debug_java_application` returns "Java Language Server not ready" or "project not detected".
After the first launch failure or timeout, do not automatically retry `debug_java_application` or relaunch through terminal commands, including when Java Language Server is not ready or the project is not detected. Report the result and diagnose the cause.

A timeout means startup is unconfirmed, not necessarily failed. You may check `get_debug_session_info` once and inspect existing terminal output; do not enter a polling loop or terminate the original launch just because the wait expired.

Only start a new launch attempt after fixing an identified cause or when the user explicitly requests a retry. Check for an existing session first and do not replace it without explicit restart intent.
6 changes: 4 additions & 2 deletions resources/skills/java-debug-inspection/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ These language model tools are contributed by the `Debugger for Java` extension
- The program is a non-Java language → do not load this skill
- The user is editing source code without an active debug session → do nothing

## Fallback
## Failure handling

If a tool returns "Java Language Server not ready" or repeats the same error twice, report the raw error to the user and stop calling debug tools for the current turn. Do not retry more than twice.
After the first failed inspection or control operation, report the error and diagnose its cause rather than automatically repeating the failed operation. Read-only diagnosis is allowed, but do not enter a polling loop.

Do not relaunch the application or fall back to terminal launch commands to recover from an inspection error. For launch failures or timeouts, follow `java-launch-troubleshooting`: a new launch attempt requires an identified cause to be fixed or an explicit user retry request, with an existing-session check first.
12 changes: 8 additions & 4 deletions resources/skills/java-launch-troubleshooting/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ These language model tools are contributed by the `Debugger for Java` extension
1. **Confirm intent.** Is the user trying to *run / start / launch / stop* a Java program (use this skill) or just edit code (do not load this skill)?
2. **Check existing session.** Call `get_debug_session_info` first. If a session is already running for the target, do not launch a second one.
3. **Launch.** Call `debug_java_application` with `target` = the fully qualified main class or JAR, and `workspacePath` = the project root containing `pom.xml`, `build.gradle`, or `.classpath`. Let `skipBuild` default to `false` so the tool handles compilation.
4. **Read the error.** If `debug_java_application` fails, the error message is structured (mainClass missing, classpath unresolved, build failure with line number). Use it to suggest a fix — do not retry with `run_in_terminal`.
4. **Stop on the first failure or timeout.** Report the returned result and diagnose the cause. Do not automatically retry `debug_java_application` or relaunch through `run_in_terminal`. A timeout means startup is unconfirmed, not necessarily failed; follow the failure-handling rules below.
5. **Stop when done.** When the user says "stop", "kill it", or has the answer they need, call `stop_debug_session`.

## Common Failure Modes
Expand All @@ -36,7 +36,7 @@ These language model tools are contributed by the `Debugger for Java` extension
|---|---|---|
| `mainClass is not configured` / `mainClass missing` | Project has no `launch.json`, and the file has no `public static void main` | Ask user which class to launch, or generate `launch.json` |
| `Could not resolve classpath` | Maven/Gradle import has not completed, or `pom.xml` has unresolved dependencies | Wait for Java Language Server import, then ask user to run `Java: Clean Java Language Server Workspace` |
| `Compilation failed` with file:line | Source code has a compile error | Fix the reported error in the source file, do not retry the launch |
| `Compilation failed` with file:line | Source code has a compile error | Fix the reported compilation error before attempting another launch; follow the failure-handling rules below |
| `Project not detected` | `workspacePath` does not contain a build file | Re-check `workspacePath`; for multi-module projects, use the module root, not the repo root |

## When NOT to Use This Skill
Expand All @@ -45,6 +45,10 @@ These language model tools are contributed by the `Debugger for Java` extension
- The user is already inside a live debug session and wants to inspect variables, evaluate expressions, walk the stack, step, or set / remove breakpoints → use `java-debug-inspection` instead, do not re-launch
- The program is a non-Java language → do not load this skill

## Fallback
## Failure handling

If `debug_java_application` returns `Java Language Server not ready` or repeats the same error twice, fall back to `run_in_terminal` with the appropriate `mvn` or `gradle` command and report the raw output to the user. Do not retry the debug tool more than twice.
After the first launch failure or timeout, stop automatic launch attempts, including when Java Language Server is not ready or the project is not detected. Do not use `run_in_terminal`, `mvn`, `gradle`, or raw `java` commands to bypass this rule.

You may inspect existing errors and terminal output. After a timeout, you may call `get_debug_session_info` once to check whether the original launch has become active. Do not enter a polling loop or terminate the original launch merely because the wait expired.

A new launch attempt is allowed only after fixing an identified cause or when the user explicitly requests a retry. Before that attempt, check for an existing session; do not replace an active session without explicit restart intent.
Comment thread
wenytang-ms marked this conversation as resolved.
50 changes: 20 additions & 30 deletions src/languageModelTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ const CONSTANTS = {
MAX_FILE_SEARCH_DEPTH: 10
};

const LAUNCH_FAILURE_GUIDANCE = '\n\nDo not automatically retry debug_java_application or start the program again '
+ 'through a terminal command. Report the result and diagnose the cause first. '
+ 'After a timeout, you may check get_debug_session_info once and inspect existing terminal output; '
+ 'do not enter a polling loop or stop the original launch just because the wait expired. '
+ 'Only start a new attempt after fixing an identified cause or when the user explicitly requests a retry. '
+ 'Before a new attempt, check whether the original launch has become active to avoid replacing it.';

// ----------------------------------------------------------------------------
// Process-wide context probed lazily on first use. The value is constant for
// the VS Code session lifetime, so we cache it.
Expand Down Expand Up @@ -131,7 +138,8 @@ export function registerLanguageModelTool(
new vscode.LanguageModelTextPart(
`Java No-Config Debug is disabled by ${ENABLE_NO_CONFIG_DEBUG}. `
+ "To use this tool, enable that setting, reload VS Code, and recreate existing terminals. "
+ "Standard Java launch/attach debugging remains available.",
+ "Standard Java launch/attach debugging remains available."
+ LAUNCH_FAILURE_GUIDANCE,
),
]);
}
Expand All @@ -152,18 +160,18 @@ export function registerLanguageModelTool(

try {
const result = await debugJavaApplication(options.input, token, guard);
if (!result.success) {
outcome = result.status === 'timeout' ? 'timeout' : 'failure';
errorCategory = result.success ? undefined : classifyError(result.message);
} else if (result.status === 'timeout') {
if (result.status === 'timeout') {
outcome = 'timeout';
errorCategory = 'timeout';
} else if (!result.success) {
outcome = 'failure';
errorCategory = classifyError(result.message);
}

// Format the message for AI - use simple text, not JSON
const message = result.success
? `✓ ${result.message}`
: `✗ ${result.message}`;
: `✗ ${result.message}${LAUNCH_FAILURE_GUIDANCE}`;

// Return result in the expected format - simple text part
return new (vscode as any).LanguageModelToolResult([
Expand All @@ -177,7 +185,7 @@ export function registerLanguageModelTool(
const errorMessage = error instanceof Error ? error.message : String(error);

return new (vscode as any).LanguageModelToolResult([
new (vscode as any).LanguageModelTextPart(`✗ Debug failed: ${errorMessage}`)
new (vscode as any).LanguageModelTextPart(`✗ Debug failed: ${errorMessage}${LAUNCH_FAILURE_GUIDANCE}`)
]);
} finally {
recordToolInvocation({
Expand Down Expand Up @@ -417,18 +425,8 @@ async function debugJavaApplication(
status: 'timeout',
message: `⏳ Debug session not yet detected for ${targetInfo} after `
+ `${CONSTANTS.SESSION_WAIT_TIMEOUT / 1000} seconds.\n\n`
+ `This is often transient — the JVM may still be starting up (large `
+ `projects, cold class-loading, or remote workspaces can need additional `
+ `time). Telemetry shows that retrying a timed-out launch succeeds for `
+ `the majority of cases.\n\n`
+ `Recommended next actions (in order):\n`
+ `1. Call debug_java_application again — most timeout cases recover on retry.\n`
+ `2. Call get_debug_session_info() to check whether the session has since `
+ `become active.\n`
+ `3. If retrying still times out, inspect terminal '${terminal.name}' for `
+ `compilation errors, ClassNotFoundException, NoClassDefFoundError, or `
+ `other startup failures.\n`
+ `4. Verify the target class name and classpath are correct, then retry.`
+ `Startup is unconfirmed, not necessarily failed. The original command may `
+ `still be running in terminal '${terminal.name}'.`
+ `${warningNote}`,
terminalName: terminal.name
});
Expand Down Expand Up @@ -480,20 +478,12 @@ async function debugJavaApplication(
guard?.markOutcomeRecorded();

return {
success: true,
success: false,
status: 'timeout',
message: `⏳ Debug command sent for ${targetInfo}; session not yet detected within `
+ `${CONSTANTS.SMART_POLLING_MAX_WAIT / 1000} seconds.\n\n`
+ `This is often transient — the application may still be starting in terminal `
+ `'${terminal.name}'. Telemetry shows that retrying or polling for status is more `
+ `likely to succeed than treating this as a permanent failure.\n\n`
+ `Recommended next actions (in order):\n`
+ `1. Call get_debug_session_info() to check whether the session has since become active.\n`
+ `2. Call debug_java_application again — most timeout cases recover on retry. `
+ `In the input arguments, set "waitForSession": true (JSON object syntax) to `
+ `extend the wait window for slow-starting apps.\n`
+ `3. If retrying still times out, inspect terminal '${terminal.name}' for compilation `
+ `errors or startup failures, then retry.${warningNote}`,
+ `Startup is unconfirmed, not necessarily failed. The original command may `
+ `still be running in terminal '${terminal.name}'.${warningNote}`,
terminalName: terminal.name
};
}
Expand Down
Loading
Loading