The onErrorOccurred hook is called when errors occur during session execution. Use it to:
import type { ErrorOccurredHookInput, HookInvocation, ErrorOccurredHookOutput } from "@github/copilot-sdk";
type ErrorOccurredHandler = (
input: ErrorOccurredHookInput,
invocation: HookInvocation
) => Promise<ErrorOccurredHookOutput | null | undefined>;
type ErrorOccurredHandler = (
input: ErrorOccurredHookInput,
invocation: HookInvocation
) => Promise<ErrorOccurredHookOutput | null | undefined>;
from copilot.session import ErrorOccurredHookInput, ErrorOccurredHookOutput
from typing import Callable, Awaitable
ErrorOccurredHandler = Callable[
[ErrorOccurredHookInput, dict[str, str]],
Awaitable[ErrorOccurredHookOutput | None]
]
ErrorOccurredHandler = Callable[
[ErrorOccurredHookInput, dict[str, str]],
Awaitable[ErrorOccurredHookOutput | None]
]
package main
import copilot "github.com/github/copilot-sdk/go"
type ErrorOccurredHandler func(
input copilot.ErrorOccurredHookInput,
invocation copilot.HookInvocation,
) (*copilot.ErrorOccurredHookOutput, error)
func main() {}
type ErrorOccurredHandler func(
input ErrorOccurredHookInput,
invocation HookInvocation,
) (*ErrorOccurredHookOutput, error)
using GitHub.Copilot.SDK;
public delegate Task<ErrorOccurredHookOutput?> ErrorOccurredHandler(
ErrorOccurredHookInput input,
HookInvocation invocation);
public delegate Task<ErrorOccurredHookOutput?> ErrorOccurredHandler(
ErrorOccurredHookInput input,
HookInvocation invocation);
// Note: Java SDK does not have an onErrorOccurred hook.
// Use EventErrorPolicy and EventErrorHandler instead:
//
// session.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);
// session.setEventErrorHandler((event, ex) -> {
// System.err.println("Error in " + event.getType() + ": " + ex.getMessage());
// });
//
// See the "Basic Error Logging" example below for a complete snippet.
type ErrorOccurredHandler = Box<dyn Fn(
ErrorOccurredHookInput,
HookInvocation,
) -> Pin<Box<dyn Future<Output = Option<ErrorOccurredHookOutput>>>> + Send + Sync>;
# on_error_occurred receives input hash and invocation hash,
# returns an ErrorOccurredHookOutput hash or nil.
on_error_occurred = ->(input, invocation) { nil }
// ErrorOccurredHandler receives $input and $invocation arrays,
// returns an ErrorOccurredHookOutput array or null.
$onErrorOccurred = function (array $input, array $invocation): ?array {
return null;
};
typealias ErrorOccurredHandler = (
ErrorOccurredHookInput,
HookInvocation
) async throws -> ErrorOccurredHookOutput?
typealias ErrorOccurredHandler = suspend (
input: ErrorOccurredHookInput,
invocation: HookInvocation
) -> ErrorOccurredHookOutput?
using ErrorOccurredHandler = std::function<
std::optional<ErrorOccurredHookOutput>(
const ErrorOccurredHookInput& input,
const HookInvocation& invocation
)>;
40 languages supported. See the full SDK list with cookbooks for Objective-C, F#, Groovy, Julia, COBOL, OCaml, Zig, Nim, D, Erlang, Crystal, Tcl, Solidity, V, and 18 more.
| Field | Type | Description |
|---|---|---|
timestamp | number | Unix timestamp when the error occurred |
cwd | string | Current working directory |
error | string | Error message |
errorContext | string | Where the error occurred: "model_call", "tool_execution", "system", or "user_input" |
recoverable | boolean | Whether the error can potentially be recovered from |
Return null or undefined to use default error handling. Otherwise, return an object with:
| Field | Type | Description |
|---|---|---|
suppressOutput | boolean | If true, don't show error output to user |
errorHandling | string | How to handle: "retry", "skip", or "abort" |
retryCount | number | Number of times to retry (if errorHandling is "retry") |
userNotification | string | Custom message to show the user |
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input, invocation) => {
console.error(`[${invocation.sessionId}] Error: ${input.error}`);
console.error(` Context: ${input.errorContext}`);
console.error(` Recoverable: ${input.recoverable}`);
return null;
},
},
});
from copilot.session import PermissionHandler
async def on_error_occurred(input_data, invocation):
print(f"[{invocation['session_id']}] Error: {input_data['error']}")
print(f" Context: {input_data['errorContext']}")
print(f" Recoverable: {input_data['recoverable']}")
return None
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={"on_error_occurred": on_error_occurred})
package main
import (
"context"
"fmt"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
client := copilot.NewClient(nil)
session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
Hooks: &copilot.SessionHooks{
OnErrorOccurred: func(input copilot.ErrorOccurredHookInput, inv copilot.HookInvocation) (*copilot.ErrorOccurredHookOutput, error) {
fmt.Printf("[%s] Error: %s\n", inv.SessionID, input.Error)
fmt.Printf(" Context: %s\n", input.ErrorContext)
fmt.Printf(" Recoverable: %v\n", input.Recoverable)
return nil, nil
},
},
})
_ = session
}
session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{
Hooks: &copilot.SessionHooks{
OnErrorOccurred: func(input copilot.ErrorOccurredHookInput, inv copilot.HookInvocation) (*copilot.ErrorOccurredHookOutput, error) {
fmt.Printf("[%s] Error: %s\n", inv.SessionID, input.Error)
fmt.Printf(" Context: %s\n", input.ErrorContext)
fmt.Printf(" Recoverable: %v\n", input.Recoverable)
return nil, nil
},
},
})
using GitHub.Copilot.SDK;
public static class ErrorHandlingExample
{
public static async Task Main()
{
await using var client = new CopilotClient();
var session = await client.CreateSessionAsync(new SessionConfig
{
Hooks = new SessionHooks
{
OnErrorOccurred = (input, invocation) =>
{
Console.Error.WriteLine($"[{invocation.SessionId}] Error: {input.Error}");
Console.Error.WriteLine($" Context: {input.ErrorContext}");
Console.Error.WriteLine($" Recoverable: {input.Recoverable}");
return Task.FromResult<ErrorOccurredHookOutput?>(null);
},
},
});
}
}
var session = await client.CreateSessionAsync(new SessionConfig
{
Hooks = new SessionHooks
{
OnErrorOccurred = (input, invocation) =>
{
Console.Error.WriteLine($"[{invocation.SessionId}] Error: {input.Error}");
Console.Error.WriteLine($" Context: {input.ErrorContext}");
Console.Error.WriteLine($" Recoverable: {input.Recoverable}");
return Task.FromResult<ErrorOccurredHookOutput?>(null);
},
},
});
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;
// Note: Java SDK does not have an onErrorOccurred hook.
// Use EventErrorPolicy and EventErrorHandler instead:
var session = client.createSession(
new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
session.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);
session.setEventErrorHandler((event, ex) -> {
System.err.println("[" + session.getSessionId() + "] Error: " + ex.getMessage());
System.err.println(" Event: " + event.getType());
});
let session = client.create_session(&SessionConfig {
hooks: Some(SessionHooks {
on_error_occurred: Some(Box::new(|input, invocation| {
Box::pin(async move {
eprintln!("[{}] Error: {}", invocation.session_id, input.error);
eprintln!(" Context: {}", input.error_context);
eprintln!(" Recoverable: {}", input.recoverable);
None
})
})),
..Default::default()
}),
..Default::default()
}).await?;
session = client.create_session(
on_permission_request: ->(_req, _inv) { { kind: "approved" } },
hooks: {
on_error_occurred: ->(input, invocation) {
warn "[#{invocation[:session_id]}] Error: #{input[:error]}"
warn " Context: #{input[:errorContext]}"
warn " Recoverable: #{input[:recoverable]}"
nil
}
}
)
$session = $client->createSession([
'hooks' => [
'onErrorOccurred' => function (array $input, array $invocation): ?array {
fwrite(STDERR, "[{$invocation['sessionId']}] Error: {$input['error']}\n");
fwrite(STDERR, " Context: {$input['errorContext']}\n");
fwrite(STDERR, " Recoverable: {$input['recoverable']}\n");
return null;
},
],
]);
let session = try await client.createSession(config: SessionConfig(
hooks: SessionHooks(
onErrorOccurred: { input, invocation in
print("[\\(invocation.sessionId)] Error: \\(input.error)", to: &stderr)
print(" Context: \\(input.errorContext)", to: &stderr)
print(" Recoverable: \\(input.recoverable)", to: &stderr)
return nil
}
)
))
val session = client.createSession(SessionConfig(
hooks = SessionHooks(
onErrorOccurred = { input, invocation ->
System.err.println("[${invocation.sessionId}] Error: ${input.error}")
System.err.println(" Context: ${input.errorContext}")
System.err.println(" Recoverable: ${input.recoverable}")
null
}
)
))
auto session = client.createSession({
.hooks = {
.onErrorOccurred = [](const ErrorOccurredHookInput& input,
const HookInvocation& invocation)
-> std::optional<ErrorOccurredHookOutput> {
std::cerr << "[" << invocation.sessionId << "] Error: " << input.error << "\n";
std::cerr << " Context: " << input.errorContext << "\n";
std::cerr << " Recoverable: " << input.recoverable << "\n";
return std::nullopt;
},
},
});
import { captureException } from "@sentry/node"; // or your monitoring service
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input, invocation) => {
captureException(new Error(input.error), {
tags: {
sessionId: invocation.sessionId,
errorContext: input.errorContext,
},
extra: {
error: input.error,
recoverable: input.recoverable,
cwd: input.cwd,
},
});
return null;
},
},
});
const ERROR_MESSAGES: Record<string, string> = {
"model_call": "There was an issue communicating with the AI model. Please try again.",
"tool_execution": "A tool failed to execute. Please check your inputs and try again.",
"system": "A system error occurred. Please try again later.",
"user_input": "There was an issue with your input. Please check and try again.",
};
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input) => {
const friendlyMessage = ERROR_MESSAGES[input.errorContext];
if (friendlyMessage) {
return {
userNotification: friendlyMessage,
};
}
return null;
},
},
});
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input) => {
// Suppress tool execution errors that are recoverable
if (input.errorContext === "tool_execution" && input.recoverable) {
console.log(`Suppressed recoverable error: ${input.error}`);
return { suppressOutput: true };
}
return null;
},
},
});
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input) => {
if (input.errorContext === "tool_execution") {
return {
userNotification: `
The tool failed. Here are some recovery suggestions:
- Check if required dependencies are installed
- Verify file paths are correct
- Try a simpler approach
`.trim(),
};
}
if (input.errorContext === "model_call" && input.error.includes("rate")) {
return {
errorHandling: "retry",
retryCount: 3,
userNotification: "Rate limit hit. Retrying...",
};
}
return null;
},
},
});
interface ErrorStats {
count: number;
lastOccurred: number;
contexts: string[];
}
const errorStats = new Map<string, ErrorStats>();
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input, invocation) => {
const key = `${input.errorContext}:${input.error.substring(0, 50)}`;
const existing = errorStats.get(key) || {
count: 0,
lastOccurred: 0,
contexts: [],
};
existing.count++;
existing.lastOccurred = input.timestamp;
existing.contexts.push(invocation.sessionId);
errorStats.set(key, existing);
// Alert if error is recurring
if (existing.count >= 5) {
console.warn(`Recurring error detected: ${key} (${existing.count} times)`);
}
return null;
},
},
});
const CRITICAL_CONTEXTS = ["system", "model_call"];
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input, invocation) => {
if (CRITICAL_CONTEXTS.includes(input.errorContext) && !input.recoverable) {
await sendAlert({
level: "critical",
message: `Critical error in session ${invocation.sessionId}`,
error: input.error,
context: input.errorContext,
timestamp: new Date(input.timestamp).toISOString(),
});
}
return null;
},
},
});
const sessionContext = new Map<string, { lastTool?: string; lastPrompt?: string }>();
const session = await client.createSession({
hooks: {
onPreToolUse: async (input, invocation) => {
const ctx = sessionContext.get(invocation.sessionId) || {};
ctx.lastTool = input.toolName;
sessionContext.set(invocation.sessionId, ctx);
return { permissionDecision: "allow" };
},
onUserPromptSubmitted: async (input, invocation) => {
const ctx = sessionContext.get(invocation.sessionId) || {};
ctx.lastPrompt = input.prompt.substring(0, 100);
sessionContext.set(invocation.sessionId, ctx);
return null;
},
onErrorOccurred: async (input, invocation) => {
const ctx = sessionContext.get(invocation.sessionId);
console.error(`Error in session ${invocation.sessionId}:`);
console.error(` Error: ${input.error}`);
console.error(` Context: ${input.errorContext}`);
if (ctx?.lastTool) {
console.error(` Last tool: ${ctx.lastTool}`);
}
if (ctx?.lastPrompt) {
console.error(` Last prompt: ${ctx.lastPrompt}...`);
}
return null;
},
},
});
Always log errors - Even if you suppress them from users, keep logs for debugging.
Categorize errors - Use errorType to handle different errors appropriately.
Don't swallow critical errors - Only suppress errors you're certain are non-critical.
Keep hooks fast - Error handling shouldn't slow down recovery.
Provide helpful context - When errors occur, additionalContext can help the model recover.
Monitor error patterns - Track recurring errors to identify systemic issues.
Can you improve this documentation? These fine people already did:
Patrick Nikoletich, Brett Cannon, Jeremiah Isaacson & Bruno BorgesEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |