Skip to content

Commit d069e01

Browse files
CopilotsunbryeCopilotpatniko
authored
Add Copilot SDK hooks documentation (#60431)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: sunbrye <56200261+sunbrye@users.noreply.github.com> Co-authored-by: sunbrye <sunbrye@github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Patrick Nikoletich <patniko@github.com>
1 parent d769c78 commit d069e01

File tree

9 files changed

+1680
-0
lines changed

9 files changed

+1680
-0
lines changed

content/copilot/how-tos/copilot-sdk/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ versions:
66
feature: copilot
77
children:
88
- /sdk-getting-started
9+
- /use-hooks
910
contentType: how-tos
1011
---
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
---
2+
title: Error handling hook
3+
shortTitle: Error handling
4+
intro: 'Use the `onErrorOccurred` hook to implement custom error logging, track error patterns, and provide user-friendly error messages in {% data variables.copilot.copilot_sdk_short %}.'
5+
product: '{% data reusables.gated-features.copilot-sdk %}'
6+
versions:
7+
feature: copilot
8+
contentType: how-tos
9+
category:
10+
- Author and optimize with Copilot
11+
---
12+
13+
> [!NOTE]
14+
> {% data reusables.copilot.copilot-sdk.technical-preview-note %}
15+
16+
The `onErrorOccurred` hook is called when errors occur during session execution. Use it to:
17+
18+
* Implement custom error logging
19+
* Track error patterns
20+
* Provide user-friendly error messages
21+
* Trigger alerts for critical errors
22+
23+
## Hook signature
24+
25+
```typescript
26+
import type { ErrorOccurredHookInput, HookInvocation, ErrorOccurredHookOutput } from "@github/copilot-sdk";
27+
type ErrorOccurredHandler = (
28+
input: ErrorOccurredHookInput,
29+
invocation: HookInvocation
30+
) => Promise<
31+
ErrorOccurredHookOutput | null | undefined
32+
>;
33+
```
34+
35+
For hook signatures in Python, Go, and .NET, see the [`github/copilot-sdk` repository](https://github.com/github/copilot-sdk/blob/main/docs/hooks/error-handling.md#hook-signature).
36+
37+
## Input
38+
39+
| Field | Type | Description |
40+
|-------|------|-------------|
41+
| `timestamp` | number | Unix timestamp when the error occurred |
42+
| `cwd` | string | Current working directory |
43+
| `error` | string | Error message |
44+
| `errorContext` | string | Where the error occurred: `"model_call"`, `"tool_execution"`, `"system"`, or `"user_input"` |
45+
| `recoverable` | boolean | Whether the error can potentially be recovered from |
46+
47+
## Output
48+
49+
Return `null` or `undefined` to use default error handling. Otherwise, return an object with any of the following fields.
50+
51+
| Field | Type | Description |
52+
|-------|------|-------------|
53+
| `suppressOutput` | boolean | If true, don't show error output to user |
54+
| `errorHandling` | string | How to handle: `"retry"`, `"skip"`, or `"abort"` |
55+
| `retryCount` | number | Number of times to retry (if `errorHandling` is `"retry"`) |
56+
| `userNotification` | string | Custom message to show the user |
57+
58+
## Examples
59+
60+
### Basic error logging
61+
62+
```typescript
63+
const session = await client.createSession({
64+
hooks: {
65+
onErrorOccurred: async (
66+
input, invocation
67+
) => {
68+
console.error(
69+
`[${invocation.sessionId}] `
70+
+ `Error: ${input.error}`
71+
);
72+
console.error(
73+
` Context: ${input.errorContext}`
74+
);
75+
console.error(
76+
` Recoverable: ${input.recoverable}`
77+
);
78+
return null;
79+
},
80+
},
81+
});
82+
```
83+
84+
For examples in Python, Go, and .NET, see the [`github/copilot-sdk` repository](https://github.com/github/copilot-sdk/blob/main/docs/hooks/error-handling.md#basic-error-logging).
85+
86+
### Send errors to monitoring service
87+
88+
```typescript
89+
import { captureException } from "@sentry/node"; // or your monitoring service
90+
91+
const session = await client.createSession({
92+
hooks: {
93+
onErrorOccurred: async (input, invocation) => {
94+
captureException(new Error(input.error), {
95+
tags: {
96+
sessionId: invocation.sessionId,
97+
errorContext: input.errorContext,
98+
},
99+
extra: {
100+
error: input.error,
101+
recoverable: input.recoverable,
102+
cwd: input.cwd,
103+
},
104+
});
105+
106+
return null;
107+
},
108+
},
109+
});
110+
```
111+
112+
### User-friendly error messages
113+
114+
```typescript
115+
const ERROR_MESSAGES: Record<string, string> = {
116+
"model_call":
117+
"There was an issue communicating "
118+
+ "with the AI model. Please try again.",
119+
"tool_execution":
120+
"A tool failed to execute. "
121+
+ "Please check your inputs and try again.",
122+
"system":
123+
"A system error occurred. "
124+
+ "Please try again later.",
125+
"user_input":
126+
"There was an issue with your input. "
127+
+ "Please check and try again.",
128+
};
129+
130+
const session = await client.createSession({
131+
hooks: {
132+
onErrorOccurred: async (input) => {
133+
const friendlyMessage =
134+
ERROR_MESSAGES[input.errorContext];
135+
136+
if (friendlyMessage) {
137+
return {
138+
userNotification: friendlyMessage,
139+
};
140+
}
141+
142+
return null;
143+
},
144+
},
145+
});
146+
```
147+
148+
### Suppress non-critical errors
149+
150+
```typescript
151+
const session = await client.createSession({
152+
hooks: {
153+
onErrorOccurred: async (input) => {
154+
// Suppress tool execution errors
155+
// that are recoverable
156+
if (
157+
input.errorContext === "tool_execution"
158+
&& input.recoverable
159+
) {
160+
console.log(
161+
`Suppressed recoverable error: `
162+
+ `${input.error}`
163+
);
164+
return { suppressOutput: true };
165+
}
166+
return null;
167+
},
168+
},
169+
});
170+
```
171+
172+
### Add recovery context
173+
174+
```typescript
175+
const session = await client.createSession({
176+
hooks: {
177+
onErrorOccurred: async (input) => {
178+
if (
179+
input.errorContext === "tool_execution"
180+
) {
181+
return {
182+
userNotification:
183+
"The tool failed. Here are some "
184+
+ "recovery suggestions:\n"
185+
+ "- Check if required dependencies "
186+
+ "are installed\n"
187+
+ "- Verify file paths are correct\n"
188+
+ "- Try a simpler approach",
189+
};
190+
}
191+
192+
if (
193+
input.errorContext === "model_call"
194+
&& input.error.includes("rate")
195+
) {
196+
return {
197+
errorHandling: "retry",
198+
retryCount: 3,
199+
userNotification:
200+
"Rate limit hit. Retrying...",
201+
};
202+
}
203+
204+
return null;
205+
},
206+
},
207+
});
208+
```
209+
210+
### Track error patterns
211+
212+
```typescript
213+
interface ErrorStats {
214+
count: number;
215+
lastOccurred: number;
216+
contexts: string[];
217+
}
218+
219+
const errorStats =
220+
new Map<string, ErrorStats>();
221+
222+
const session = await client.createSession({
223+
hooks: {
224+
onErrorOccurred: async (
225+
input, invocation
226+
) => {
227+
const key =
228+
`${input.errorContext}:`
229+
+ `${input.error.substring(0, 50)}`;
230+
231+
const existing =
232+
errorStats.get(key) || {
233+
count: 0,
234+
lastOccurred: 0,
235+
contexts: [],
236+
};
237+
238+
existing.count++;
239+
existing.lastOccurred = input.timestamp;
240+
existing.contexts.push(
241+
invocation.sessionId
242+
);
243+
244+
errorStats.set(key, existing);
245+
246+
// Alert if error is recurring
247+
if (existing.count >= 5) {
248+
console.warn(
249+
`Recurring error detected: `
250+
+ `${key} (${existing.count} times)`
251+
);
252+
}
253+
254+
return null;
255+
},
256+
},
257+
});
258+
```
259+
260+
### Alert on critical errors
261+
262+
```typescript
263+
const CRITICAL_CONTEXTS = [
264+
"system", "model_call",
265+
];
266+
267+
const session = await client.createSession({
268+
hooks: {
269+
onErrorOccurred: async (
270+
input, invocation
271+
) => {
272+
if (
273+
CRITICAL_CONTEXTS.includes(
274+
input.errorContext
275+
)
276+
&& !input.recoverable
277+
) {
278+
await sendAlert({
279+
level: "critical",
280+
message:
281+
`Critical error in session `
282+
+ `${invocation.sessionId}`,
283+
error: input.error,
284+
context: input.errorContext,
285+
timestamp: new Date(
286+
input.timestamp
287+
).toISOString(),
288+
});
289+
}
290+
291+
return null;
292+
},
293+
},
294+
});
295+
```
296+
297+
## Best practices
298+
299+
* **Always log errors.** Even if you suppress them from users, keep logs for debugging.
300+
* **Categorize errors.** Use `errorContext` to handle different errors appropriately.
301+
* **Don't swallow critical errors.** Only suppress errors you're certain are non-critical.
302+
* **Keep hooks fast.** Error handling shouldn't slow down recovery.
303+
* **Monitor error patterns.** Track recurring errors to identify systemic issues.
304+
305+
## Further reading
306+
307+
* [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/quickstart)
308+
* [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/session-lifecycle)
309+
* [AUTOTITLE](/copilot/how-tos/copilot-sdk/use-hooks/pre-tool-use)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: Use hooks
3+
shortTitle: Use hooks
4+
intro: 'Customize the behavior of {% data variables.copilot.copilot_sdk_short %} sessions at key points in the conversation lifecycle using hooks.'
5+
versions:
6+
feature: copilot
7+
children:
8+
- /quickstart
9+
- /pre-tool-use
10+
- /post-tool-use
11+
- /user-prompt-submitted
12+
- /session-lifecycle
13+
- /error-handling
14+
contentType: how-tos
15+
---

0 commit comments

Comments
 (0)