# Крюк для обработки ошибок

onErrorOccurred Крюк вызывается, когда во время выполнения сессии возникают ошибки. Он используется для следующих задач:

<!-- markdownlint-disable GHD046 GHD005 -->

<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->

* Реализовать пользовательский журнал ошибок
* Паттерны ошибок трека
* Предоставлять удобные сообщения об ошибках
* Триггерные оповещения о критических ошибках

## Сигнатура крюка

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
import type { ErrorOccurredHookInput, HookInvocation, ErrorOccurredHookOutput } from "@github/copilot-sdk";
type ErrorOccurredHandler = (
  input: ErrorOccurredHookInput,
  invocation: HookInvocation
) => Promise<ErrorOccurredHookOutput | null | undefined>;
```

```typescript
type ErrorOccurredHandler = (
  input: ErrorOccurredHookInput,
  invocation: HookInvocation
) => Promise<ErrorOccurredHookOutput | null | undefined>;
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
from copilot.session import ErrorOccurredHookInput, ErrorOccurredHookOutput
from typing import Callable, Awaitable

ErrorOccurredHandler = Callable[
    [ErrorOccurredHookInput, dict[str, str]],
    Awaitable[ErrorOccurredHookOutput | None]
]
```

```python
ErrorOccurredHandler = Callable[
    [ErrorOccurredHookInput, dict[str, str]],
    Awaitable[ErrorOccurredHookOutput | None]
]
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
package main

import copilot "github.com/github/copilot-sdk/go"

type ErrorOccurredHandler func(
    input copilot.ErrorOccurredHookInput,
    invocation copilot.HookInvocation,
) (*copilot.ErrorOccurredHookOutput, error)

func main() {}
```

```golang
type ErrorOccurredHandler func(
    input ErrorOccurredHookInput,
    invocation HookInvocation,
) (*ErrorOccurredHookOutput, error)
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
using GitHub.Copilot;

public delegate Task<ErrorOccurredHookOutput?> ErrorOccurredHandler(
    ErrorOccurredHookInput input,
    HookInvocation invocation);
```

```csharp
public delegate Task<ErrorOccurredHookOutput?> ErrorOccurredHandler(
    ErrorOccurredHookInput input,
    HookInvocation invocation);
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

<!-- docs-validate: skip -->

```java
// 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.
```

</div>

</div>

## Input

| Поле           | Тип     | Description                                                                              |
| -------------- | ------- | ---------------------------------------------------------------------------------------- |
| `timestamp`    | Номер   | Временная метка Unix в момент возникшей ошибки                                           |
| `cwd`          | струна  | Текущий рабочий справочник                                                               |
| `error`        | струна  | Сообщение об ошибке                                                                      |
| `errorContext` | струна  | Где произошла ошибка: `"model_call"`, `"tool_execution"`, `"system"`, или `"user_input"` |
| `recoverable`  | boolean | Можно ли потенциально восстановить ошибку из                                             |

## Output

Возврат `null` или `undefined` использование обработки ошибок по умолчанию. В противном случае вернём объект с:

| Поле               | Тип     | Description                                                 |
| ------------------ | ------- | ----------------------------------------------------------- |
| `suppressOutput`   | boolean | Если это верно, не показывайте ошибку пользователю          |
| `errorHandling`    | струна  | Как справиться: `"retry"`, `"skip"`, или `"abort"`          |
| `retryCount`       | Номер   | Количество повторных попыток (если errorHandling )`"retry"` |
| `userNotification` | струна  | Пользовательское сообщение для показа пользователю          |

## Примеры

### Базовое ведение ошибок

<div class="ghd-codetabs">
<div class="ghd-codetab" data-lang="typescript" data-label="TypeScript"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">TypeScript</div>

```typescript
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;
    },
  },
});
```

</div>

<div class="ghd-codetab" data-lang="python" data-label="Python"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Python</div>

```python
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})
```

</div>

<div class="ghd-codetab" data-lang="go" data-label="Go"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Go</div>

```golang
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
}
```

```golang
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
        },
    },
})
```

</div>

<div class="ghd-codetab" data-lang="dotnet" data-label=".NET"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">.NET</div>

```csharp
using GitHub.Copilot;

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);
                },
            },
        });
    }
}
```

```csharp
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);
        },
    },
});
```

</div>

<div class="ghd-codetab" data-lang="java" data-label="Java"><div class="ghd-codetab-fallback-label" role="heading" aria-level="3">Java</div>

<!-- docs-validate: skip -->

```java
import com.github.copilot.*;
import com.github.copilot.rpc.*;

// 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());
});
```

</div>

</div>

### Отправьте ошибки в сервис мониторинга

```typescript
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;
    },
  },
});
```

### Понятные пользователю сообщения об ошибках

```typescript
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;
    },
  },
});
```

### Подавление некритических ошибок

```typescript
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;
    },
  },
});
```

### Добавить контекст восстановления

```typescript
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;
    },
  },
});
```

### Паттерны ошибок трека

```typescript
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;
    },
  },
});
```

### Предупреждение о критических ошибках

```typescript
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;
    },
  },
});
```

### Комбинируйте с другими зацепками для контекста

```typescript
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;
    },
  },
});
```

## Лучшие практики

1. **Всегда фиксируйте ошибки** — даже если вы их подавляете от пользователей, ведите логи для отладки.

2. **Категоризировать ошибки** — использовать `errorType` для правильной обработки различных ошибок.

3. **Не принимайте критические ошибки** — подавляйте только те ошибки, в которых уверены, что они не критичные.

4. **Держите крючки быстрыми** — обработка ошибок не должна замедлять восстановление.

5. **Предоставьте полезный контекст** — когда возникают ошибки, `additionalContext` это может помочь модели восстановиться.

6. **Отслеживайте шаблоны ошибок** — отслеживайте повторяющиеся ошибки для выявления системных проблем.

## См. также

* [Используйте крючки](/ru/copilot/how-tos/copilot-sdk/hooks)
* [Крючки жизненного цикла сессии](/ru/copilot/how-tos/copilot-sdk/hooks/session-lifecycle)
* [Руководство по отладке](/ru/copilot/how-tos/copilot-sdk/troubleshooting/debugging)