{"meta":{"title":"Error handling hook","intro":"The onErrorOccurred hook is called when errors occur during session execution. Use it to:","product":"GitHub Copilot","breadcrumbs":[{"href":"/en/enterprise-cloud@latest/copilot","title":"GitHub Copilot"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos","title":"How-tos"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks","title":"Use hooks"},{"href":"/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/error-handling","title":"Error Handling"}],"documentType":"article"},"body":"# Error handling hook\n\nThe onErrorOccurred hook is called when errors occur during session execution. Use it to:\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n* Implement custom error logging\n* Track error patterns\n* Provide user-friendly error messages\n* Trigger alerts for critical errors\n\n## Hook signature\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\ntype ErrorOccurredHandler = (\n  input: ErrorOccurredHookInput,\n  invocation: HookInvocation\n) => Promise<ErrorOccurredHookOutput | null | undefined>;\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nErrorOccurredHandler = Callable[\n    [ErrorOccurredHookInput, dict[str, str]],\n    Awaitable[ErrorOccurredHookOutput | None]\n]\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\ntype ErrorOccurredHandler func(\n    input ErrorOccurredHookInput,\n    invocation HookInvocation,\n) (*ErrorOccurredHookOutput, error)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\npublic delegate Task<ErrorOccurredHookOutput?> ErrorOccurredHandler(\n    ErrorOccurredHookInput input,\n    HookInvocation invocation);\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\n// Note: Java SDK does not have an onErrorOccurred hook.\n// Use EventErrorPolicy and EventErrorHandler instead:\n//\n// session.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);\n// session.setEventErrorHandler((event, ex) -> {\n//     System.err.println(\"Error in \" + event.getType() + \": \" + ex.getMessage());\n// });\n//\n// See the \"Basic Error Logging\" example below for a complete snippet.\n```\n\n</div>\n\n</div>\n\n## Input\n\n| Field          | Type    | Description                                                                                 |\n| -------------- | ------- | ------------------------------------------------------------------------------------------- |\n| `timestamp`    | number  | Unix timestamp when the error occurred                                                      |\n| `cwd`          | string  | Current working directory                                                                   |\n| `error`        | string  | Error message                                                                               |\n| `errorContext` | string  | Where the error occurred: `\"model_call\"`, `\"tool_execution\"`, `\"system\"`, or `\"user_input\"` |\n| `recoverable`  | boolean | Whether the error can potentially be recovered from                                         |\n\n## Output\n\nReturn `null` or `undefined` to use default error handling. Otherwise, return an object with:\n\n| Field              | Type    | Description                                              |\n| ------------------ | ------- | -------------------------------------------------------- |\n| `suppressOutput`   | boolean | If true, don't show error output to user                 |\n| `errorHandling`    | string  | How to handle: `\"retry\"`, `\"skip\"`, or `\"abort\"`         |\n| `retryCount`       | number  | Number of times to retry (if errorHandling is `\"retry\"`) |\n| `userNotification` | string  | Custom message to show the user                          |\n\n## Examples\n\n### Basic error logging\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input, invocation) => {\n      console.error(`[${invocation.sessionId}] Error: ${input.error}`);\n      console.error(`  Context: ${input.errorContext}`);\n      console.error(`  Recoverable: ${input.recoverable}`);\n      return null;\n    },\n  },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.session import PermissionHandler\n\nasync def on_error_occurred(input_data, invocation):\n    print(f\"[{invocation['session_id']}] Error: {input_data['error']}\")\n    print(f\"  Context: {input_data['errorContext']}\")\n    print(f\"  Recoverable: {input_data['recoverable']}\")\n    return None\n\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={\"on_error_occurred\": on_error_occurred})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\nsession, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{\n    Hooks: &copilot.SessionHooks{\n        OnErrorOccurred: func(input copilot.ErrorOccurredHookInput, inv copilot.HookInvocation) (*copilot.ErrorOccurredHookOutput, error) {\n            fmt.Printf(\"[%s] Error: %s\\n\", inv.SessionID, input.Error)\n            fmt.Printf(\"  Context: %s\\n\", input.ErrorContext)\n            fmt.Printf(\"  Recoverable: %v\\n\", input.Recoverable)\n            return nil, nil\n        },\n    },\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    Hooks = new SessionHooks\n    {\n        OnErrorOccurred = (input, invocation) =>\n        {\n            Console.Error.WriteLine($\"[{invocation.SessionId}] Error: {input.Error}\");\n            Console.Error.WriteLine($\"  Context: {input.ErrorContext}\");\n            Console.Error.WriteLine($\"  Recoverable: {input.Recoverable}\");\n            return Task.FromResult<ErrorOccurredHookOutput?>(null);\n        },\n    },\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.*;\nimport com.github.copilot.rpc.*;\n\n// Note: Java SDK does not have an onErrorOccurred hook.\n// Use EventErrorPolicy and EventErrorHandler instead:\n\nvar session = client.createSession(\n    new SessionConfig()\n        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n).get();\n\nsession.setEventErrorPolicy(EventErrorPolicy.SUPPRESS_AND_LOG_ERRORS);\nsession.setEventErrorHandler((event, ex) -> {\n    System.err.println(\"[\" + session.getSessionId() + \"] Error: \" + ex.getMessage());\n    System.err.println(\"  Event: \" + event.getType());\n});\n```\n\n</div>\n\n</div>\n\n### Send errors to monitoring service\n\n```typescript\nimport { captureException } from \"@sentry/node\"; // or your monitoring service\n\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input, invocation) => {\n      captureException(new Error(input.error), {\n        tags: {\n          sessionId: invocation.sessionId,\n          errorContext: input.errorContext,\n        },\n        extra: {\n          error: input.error,\n          recoverable: input.recoverable,\n          cwd: input.cwd,\n        },\n      });\n      \n      return null;\n    },\n  },\n});\n```\n\n### User-friendly error messages\n\n```typescript\nconst ERROR_MESSAGES: Record<string, string> = {\n  \"model_call\": \"There was an issue communicating with the AI model. Please try again.\",\n  \"tool_execution\": \"A tool failed to execute. Please check your inputs and try again.\",\n  \"system\": \"A system error occurred. Please try again later.\",\n  \"user_input\": \"There was an issue with your input. Please check and try again.\",\n};\n\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input) => {\n      const friendlyMessage = ERROR_MESSAGES[input.errorContext];\n      \n      if (friendlyMessage) {\n        return {\n          userNotification: friendlyMessage,\n        };\n      }\n      \n      return null;\n    },\n  },\n});\n```\n\n### Suppress non-critical errors\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input) => {\n      // Suppress tool execution errors that are recoverable\n      if (input.errorContext === \"tool_execution\" && input.recoverable) {\n        console.log(`Suppressed recoverable error: ${input.error}`);\n        return { suppressOutput: true };\n      }\n      return null;\n    },\n  },\n});\n```\n\n### Add recovery context\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input) => {\n      if (input.errorContext === \"tool_execution\") {\n        return {\n          userNotification: `\nThe tool failed. Here are some recovery suggestions:\n- Check if required dependencies are installed\n- Verify file paths are correct\n- Try a simpler approach\n          `.trim(),\n        };\n      }\n      \n      if (input.errorContext === \"model_call\" && input.error.includes(\"rate\")) {\n        return {\n          errorHandling: \"retry\",\n          retryCount: 3,\n          userNotification: \"Rate limit hit. Retrying...\",\n        };\n      }\n      \n      return null;\n    },\n  },\n});\n```\n\n### Track error patterns\n\n```typescript\ninterface ErrorStats {\n  count: number;\n  lastOccurred: number;\n  contexts: string[];\n}\n\nconst errorStats = new Map<string, ErrorStats>();\n\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input, invocation) => {\n      const key = `${input.errorContext}:${input.error.substring(0, 50)}`;\n      \n      const existing = errorStats.get(key) || {\n        count: 0,\n        lastOccurred: 0,\n        contexts: [],\n      };\n      \n      existing.count++;\n      existing.lastOccurred = input.timestamp;\n      existing.contexts.push(invocation.sessionId);\n      \n      errorStats.set(key, existing);\n      \n      // Alert if error is recurring\n      if (existing.count >= 5) {\n        console.warn(`Recurring error detected: ${key} (${existing.count} times)`);\n      }\n      \n      return null;\n    },\n  },\n});\n```\n\n### Alert on critical errors\n\n```typescript\nconst CRITICAL_CONTEXTS = [\"system\", \"model_call\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input, invocation) => {\n      if (CRITICAL_CONTEXTS.includes(input.errorContext) && !input.recoverable) {\n        await sendAlert({\n          level: \"critical\",\n          message: `Critical error in session ${invocation.sessionId}`,\n          error: input.error,\n          context: input.errorContext,\n          timestamp: new Date(input.timestamp).toISOString(),\n        });\n      }\n      \n      return null;\n    },\n  },\n});\n```\n\n### Combine with other hooks for context\n\n```typescript\nconst sessionContext = new Map<string, { lastTool?: string; lastPrompt?: string }>();\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input, invocation) => {\n      const ctx = sessionContext.get(invocation.sessionId) || {};\n      ctx.lastTool = input.toolName;\n      sessionContext.set(invocation.sessionId, ctx);\n      return { permissionDecision: \"allow\" };\n    },\n    \n    onUserPromptSubmitted: async (input, invocation) => {\n      const ctx = sessionContext.get(invocation.sessionId) || {};\n      ctx.lastPrompt = input.prompt.substring(0, 100);\n      sessionContext.set(invocation.sessionId, ctx);\n      return null;\n    },\n    \n    onErrorOccurred: async (input, invocation) => {\n      const ctx = sessionContext.get(invocation.sessionId);\n      \n      console.error(`Error in session ${invocation.sessionId}:`);\n      console.error(`  Error: ${input.error}`);\n      console.error(`  Context: ${input.errorContext}`);\n      if (ctx?.lastTool) {\n        console.error(`  Last tool: ${ctx.lastTool}`);\n      }\n      if (ctx?.lastPrompt) {\n        console.error(`  Last prompt: ${ctx.lastPrompt}...`);\n      }\n      \n      return null;\n    },\n  },\n});\n```\n\n## Best practices\n\n1. **Always log errors** - Even if you suppress them from users, keep logs for debugging.\n\n2. **Categorize errors** - Use `errorType` to handle different errors appropriately.\n\n3. **Don't swallow critical errors** - Only suppress errors you're certain are non-critical.\n\n4. **Keep hooks fast** - Error handling shouldn't slow down recovery.\n\n5. **Provide helpful context** - When errors occur, `additionalContext` can help the model recover.\n\n6. **Monitor error patterns** - Track recurring errors to identify systemic issues.\n\n## See also\n\n* [Use hooks](/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks)\n* [Session lifecycle hooks](/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/session-lifecycle)\n* [Debugging guide](/en/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/troubleshooting/debugging)"}