{"meta":{"title":"오류 처리 후크","intro":"onErrorOccurred 세션 실행 중에 오류가 발생할 때 후크가 호출됩니다. 이를 사용하여 다음을 수행합니다.","product":"GitHub Copilot","breadcrumbs":[{"href":"/ko/enterprise-cloud@latest/copilot","title":"GitHub Copilot"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos","title":"방법"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk","title":"코필로트 SDK"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks","title":"후크 사용"},{"href":"/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/error-handling","title":"오류 처리"}],"documentType":"article"},"body":"# 오류 처리 후크\n\nonErrorOccurred 세션 실행 중에 오류가 발생할 때 후크가 호출됩니다. 이를 사용하여 다음을 수행합니다.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n* 사용자 지정 오류 로깅 구현\n* 오류 패턴 추적\n* 사용자에게 친숙한 오류 메시지 제공\n* 중요한 오류 발생 시 경고 트리거\n\n## 후크 서명\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## 입력\n\n| Field          | Type    | Description                                                                 |\n| -------------- | ------- | --------------------------------------------------------------------------- |\n| `timestamp`    | number  | 오류가 발생한 시점의 Unix 타임스탬프                                                      |\n| `cwd`          | string  | 현재 작업 디렉터리                                                                  |\n| `error`        | string  | 오류 메시지                                                                      |\n| `errorContext` | string  | 오류가 발생한 위치: `\"model_call\"`, `\"tool_execution\"`, `\"system\"`또는 `\"user_input\"` |\n| `recoverable`  | boolean | 오류를 복구할 수 있는지 여부                                                            |\n\n## 출력\n\n기본 오류 처리를 위해 `null`를 반환하거나 `undefined`를 사용합니다. 그렇지 않으면 다음과 같은 객체를 반환합니다:\n\n| Field              | Type    | Description                            |\n| ------------------ | ------- | -------------------------------------- |\n| `suppressOutput`   | boolean | true이면 사용자에게 오류 출력을 표시하지 마세요.          |\n| `errorHandling`    | string  | 처리 방법: `\"retry\"`, `\"skip\"`또는 `\"abort\"` |\n| `retryCount`       | number  | 다시 시도할 횟수(errorHandling인 `\"retry\"`경우)  |\n| `userNotification` | string  | 사용자를 표시하는 사용자 지정 메시지                   |\n\n## 예제\n\n### 기본 오류 로깅\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### 모니터링 서비스에 오류 보내기\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### 사용자에게 친숙한 오류 메시지\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### 중요하지 않은 오류 표시 안 함\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### 복구 컨텍스트 추가\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### 오류 패턴 추적\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### 중요한 오류에 대한 경고\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### 컨텍스트를 위해 다른 훅과 함께 사용\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## 모범 사례\n\n1. **항상 오류를 기록하세요** - 사용자에게 오류를 표시하지 않더라도 디버깅을 위해 로그를 남겨 두세요.\n\n2. **오류 분류** - 다양한 오류를 적절하게 처리하는 데 사용합니다 `errorType` .\n\n3. **중요한 오류를 숨기지 마세요** - 중요하지 않은 오류라고 확신하는 경우에만 오류를 억제하세요.\n\n4. **후크를 빠르게 유지하세요** - 오류 처리가 복구를 늦춰서는 안 됩니다.\n\n5. **유용한 컨텍스트 제공** - 오류가 발생하면 `additionalContext` 모델을 복구하는 데 도움이 될 수 있습니다.\n\n6. **오류 패턴 모니터링** - 되풀이 오류를 추적하여 시스템 문제를 식별합니다.\n\n## 참고하십시오\n\n* [후크 사용](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks)\n* [세션 수명 주기 후크](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/session-lifecycle)\n* [디버깅 가이드](/ko/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/troubleshooting/debugging)"}