Files
awesome-copilot/cookbook/copilot-sdk/java/error-handling.md
Bruno Borges 46f6f3e6db Add Java SDK cookbook with 7 recipes
Add complete Java cookbook matching the pattern of existing .NET, Go,
Node.js, and Python cookbooks. All 7 recipes included:

- Ralph Loop: Autonomous AI task loops with JBang
- Error Handling: try-with-resources, ExecutionException, timeouts
- Multiple Sessions: Parallel sessions with CompletableFuture
- Managing Local Files: AI-powered file organization
- PR Visualization: Interactive PR age charts
- Persisting Sessions: Save/resume with custom IDs
- Accessibility Report: WCAG reports via Playwright MCP

Each recipe includes both markdown documentation and a standalone
JBang-runnable Java file in recipe/.
2026-04-06 15:20:20 -04:00

5.6 KiB

Error Handling Patterns

Handle errors gracefully in your Copilot SDK applications.

Runnable example: recipe/ErrorHandling.java

jbang recipe/ErrorHandling.java

Example scenario

You need to handle various error conditions like connection failures, timeouts, and invalid responses.

Basic try-with-resources

Java's try-with-resources ensures the client is always cleaned up, even when exceptions occur.

//DEPS com.github:copilot-sdk-java:0.2.1-java.1

import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;

public class BasicErrorHandling {
    public static void main(String[] args) {
        try (var client = new CopilotClient()) {
            client.start().get();
            var session = client.createSession(
                new SessionConfig()
                    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
                    .setModel("gpt-5")).get();

            var response = session.sendAndWait(
                new MessageOptions().setPrompt("Hello!")).get();
            System.out.println(response.getData().content());

            session.close();
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
        }
    }
}

Handling specific error types

Every CompletableFuture.get() call wraps failures in ExecutionException. Unwrap the cause to inspect the real error.

import java.io.IOException;
import java.util.concurrent.ExecutionException;

try (var client = new CopilotClient()) {
    client.start().get();
} catch (ExecutionException ex) {
    var cause = ex.getCause();
    if (cause instanceof IOException) {
        System.err.println("Copilot CLI not found or could not connect: " + cause.getMessage());
    } else {
        System.err.println("Unexpected error: " + cause.getMessage());
    }
} catch (InterruptedException ex) {
    Thread.currentThread().interrupt();
    System.err.println("Interrupted while starting client.");
}

Timeout handling

Use the overloaded get(timeout, unit) on CompletableFuture to enforce time limits.

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

var session = client.createSession(
    new SessionConfig()
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
        .setModel("gpt-5")).get();

try {
    var response = session.sendAndWait(
        new MessageOptions().setPrompt("Complex question..."))
        .get(30, TimeUnit.SECONDS);

    System.out.println(response.getData().content());
} catch (TimeoutException ex) {
    System.err.println("Request timed out after 30 seconds.");
    session.abort().get();
}

Aborting a request

Cancel an in-flight request by calling session.abort().

var session = client.createSession(
    new SessionConfig()
        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
        .setModel("gpt-5")).get();

// Start a request without waiting
session.send(new MessageOptions().setPrompt("Write a very long story..."));

// Abort after some condition
Thread.sleep(5000);
session.abort().get();
System.out.println("Request aborted.");

Graceful shutdown

Use a JVM shutdown hook to clean up when the process is interrupted.

var client = new CopilotClient();
client.start().get();

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    System.out.println("Shutting down...");
    try {
        client.close();
    } catch (Exception ex) {
        System.err.println("Cleanup error: " + ex.getMessage());
    }
}));

Try-with-resources (nested)

When working with multiple sessions, nest try-with-resources blocks to guarantee each resource is closed.

try (var client = new CopilotClient()) {
    client.start().get();

    try (var session = client.createSession(
            new SessionConfig()
                .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
                .setModel("gpt-5")).get()) {

        session.sendAndWait(
            new MessageOptions().setPrompt("Hello!")).get();
    } // session is closed here

} // client is closed here

Handling tool errors

When defining tools, return an error result to signal a failure back to the model instead of throwing.

import com.github.copilot.sdk.json.ToolResultObject;

session.addTool(
    ToolDefinition.builder()
        .name("read_file")
        .description("Read a file from disk")
        .parameter("path", "string", "File path", true)
        .build(),
    (args) -> {
        try {
            var content = java.nio.file.Files.readString(
                java.nio.file.Path.of(args.get("path").toString()));
            return ToolResultObject.success(content);
        } catch (IOException ex) {
            return ToolResultObject.error("Failed to read file: " + ex.getMessage());
        }
    }
);

Best practices

  1. Use try-with-resources: Always wrap CopilotClient (and sessions, if AutoCloseable) in try-with-resources to guarantee cleanup.
  2. Unwrap ExecutionException: Call getCause() to inspect the real error — the outer ExecutionException is just a CompletableFuture wrapper.
  3. Restore interrupt flag: When catching InterruptedException, call Thread.currentThread().interrupt() to preserve the interrupted status.
  4. Set timeouts: Use get(timeout, TimeUnit) instead of bare get() for any call that could block indefinitely.
  5. Return tool errors, don't throw: Use ToolResultObject.error() so the model can recover gracefully.
  6. Log errors: Capture error details for debugging — consider a logging framework like SLF4J for production applications.