Fork me on GitHub

Error Handling Patterns

Handle errors gracefully in your Copilot SDK applications.

Prerequisites

Install JBang to run these examples:

# macOS (using Homebrew)
brew install jbangdev/tap/jbang

# Linux/macOS (using curl)
curl -Ls https://sh.jbang.dev | bash -s - app setup

# Windows (using Scoop)
scoop install jbang

Example scenario

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

Basic error handling

Usage:

jbang BasicErrorHandling.java

Code:

//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.9
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
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().setModel("gpt-5")).get();

            session.on(AssistantMessageEvent.class, msg -> {
                System.out.println(msg.getData().getContent());
            });

            session.sendAndWait(new MessageOptions()
                .setPrompt("Hello!")).get();

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

Handling specific error types

//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.9
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.concurrent.ExecutionException;

public class SpecificErrorHandling {
    public static void startClient() {
        try (var client = new CopilotClient()) {
            client.start().get();
            // ... use client ...
        } catch (ExecutionException ex) {
            Throwable cause = ex.getCause();
            if (cause instanceof java.io.IOException) {
                System.err.println("Copilot CLI not found. Please install it first.");
                System.err.println("Details: " + cause.getMessage());
            } else if (cause instanceof java.util.concurrent.TimeoutException) {
                System.err.println("Could not connect to Copilot CLI server.");
                System.err.println("Details: " + cause.getMessage());
            } else {
                System.err.println("Unexpected error: " + cause.getMessage());
                cause.printStackTrace();
            }
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            System.err.println("Operation interrupted: " + ex.getMessage());
        } catch (Exception ex) {
            System.err.println("Unexpected error: " + ex.getMessage());
            ex.printStackTrace();
        }
    }
}

Timeout handling

//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.9
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class TimeoutHandling {
    public static void sendWithTimeout(CopilotSession session) {
        try {
            session.on(AssistantMessageEvent.class, msg -> {
                System.out.println(msg.getData().getContent());
            });

            // Wait up to 30 seconds for response
            session.sendAndWait(new MessageOptions()
                .setPrompt("Complex question..."))
                .get(30, TimeUnit.SECONDS);

        } catch (TimeoutException ex) {
            System.err.println("Request timed out");
        } catch (Exception ex) {
            System.err.println("Error: " + ex.getMessage());
        }
    }
}

Aborting a request

//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.9
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class AbortRequest {
    public static void abortAfterDelay(CopilotSession session) {
        // Start a request (non-blocking)
        session.send(new MessageOptions()
            .setPrompt("Write a very long story..."));

        // Schedule abort after 5 seconds
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.schedule(() -> {
            try {
                session.abort().get();
                System.out.println("Request aborted");
            } catch (Exception ex) {
                System.err.println("Failed to abort: " + ex.getMessage());
            } finally {
                scheduler.shutdown();
            }
        }, 5, TimeUnit.SECONDS);
    }
}

Graceful shutdown

//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.9
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;

public class GracefulShutdown {
    public static void main(String[] args) {
        var client = new CopilotClient();

        // Set up shutdown hook
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("\nShutting down...");
            try {
                client.close();
            } catch (Exception ex) {
                System.err.println("Error during shutdown: " + ex.getMessage());
            }
        }));

        try {
            client.start().get();
            // ... do work ...
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Try-with-resources pattern

//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.9
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;

public class TryWithResources {
    public static void doWork() throws Exception {
        try (var client = new CopilotClient()) {
            client.start().get();

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

                session.on(AssistantMessageEvent.class, msg -> {
                    System.out.println(msg.getData().getContent());
                });

                session.sendAndWait(new MessageOptions()
                    .setPrompt("Hello!")).get();

                // Session and client are automatically closed
            }
        }
    }
}

Handling tool errors

//DEPS io.github.copilot-community-sdk:copilot-sdk:1.0.9
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.Map;
import java.util.List;
import java.util.concurrent.CompletableFuture;

public class ToolErrorHandling {
    public static void handleToolErrors() throws Exception {
        var errorTool = ToolDefinition.create(
            "get_user_location",
            "Gets the user's location",
            Map.of("type", "object", "properties", Map.of()),
            invocation -> {
                // Return an error result
                return CompletableFuture.completedFuture(
                    ToolResultObject.error("Location service unavailable")
                );
            }
        );

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

            var session = client.createSession(
                new SessionConfig().setTools(List.of(errorTool))).get();

            session.on(AssistantMessageEvent.class, msg -> {
                System.out.println(msg.getData().getContent());
            });

            // Session continues even when tool fails
            session.sendAndWait(new MessageOptions()
                .setPrompt("What is my location? If you can't find out, just say 'unknown'."))
                .get();

            session.close();
        }
    }
}

Best practices

  1. Always clean up: Use try-with-resources to ensure close() is called
  2. Handle connection errors: The CLI might not be installed or running
  3. Set appropriate timeouts: Use get(timeout, TimeUnit) for long-running requests
  4. Log errors: Capture error details for debugging
  5. Wrap operations: Consider wrapping SDK operations in methods that handle common errors
  6. Check error causes: Use ExecutionException.getCause() to get the actual error from CompletableFuture