diff --git a/src/hooks/anthropic-context-window-limit-recovery/parser.test.ts b/src/hooks/anthropic-context-window-limit-recovery/parser.test.ts
new file mode 100644
index 000000000..d0fa10584
--- /dev/null
+++ b/src/hooks/anthropic-context-window-limit-recovery/parser.test.ts
@@ -0,0 +1,97 @@
+///
+import { describe, expect, it } from "bun:test"
+import { parseAnthropicTokenLimitError } from "./parser"
+
+describe("parseAnthropicTokenLimitError", () => {
+ it("#given a standard token limit error string #when parsing #then extracts tokens", () => {
+ //#given
+ const error = "prompt is too long: 250000 tokens > 200000 maximum"
+
+ //#when
+ const result = parseAnthropicTokenLimitError(error)
+
+ //#then
+ expect(result).not.toBeNull()
+ expect(result!.currentTokens).toBe(250000)
+ expect(result!.maxTokens).toBe(200000)
+ })
+
+ it("#given a non-token-limit error #when parsing #then returns null", () => {
+ //#given
+ const error = { message: "internal server error" }
+
+ //#when
+ const result = parseAnthropicTokenLimitError(error)
+
+ //#then
+ expect(result).toBeNull()
+ })
+
+ it("#given null input #when parsing #then returns null", () => {
+ //#given
+ const error = null
+
+ //#when
+ const result = parseAnthropicTokenLimitError(error)
+
+ //#then
+ expect(result).toBeNull()
+ })
+
+ it("#given a proxy error with non-standard structure #when parsing #then returns null without crashing", () => {
+ //#given
+ const proxyError = {
+ data: [1, 2, 3],
+ error: "string-not-object",
+ message: "Failed to process error response",
+ }
+
+ //#when
+ const result = parseAnthropicTokenLimitError(proxyError)
+
+ //#then
+ expect(result).toBeNull()
+ })
+
+ it("#given a circular reference error #when parsing #then returns null without crashing", () => {
+ //#given
+ const circular: Record = { message: "prompt is too long" }
+ circular.self = circular
+
+ //#when
+ const result = parseAnthropicTokenLimitError(circular)
+
+ //#then
+ expect(result).not.toBeNull()
+ })
+
+ it("#given an error where data.responseBody has invalid JSON #when parsing #then handles gracefully", () => {
+ //#given
+ const error = {
+ data: { responseBody: "not valid json {{{" },
+ message: "prompt is too long with 300000 tokens exceeds 200000",
+ }
+
+ //#when
+ const result = parseAnthropicTokenLimitError(error)
+
+ //#then
+ expect(result).not.toBeNull()
+ expect(result!.currentTokens).toBe(300000)
+ expect(result!.maxTokens).toBe(200000)
+ })
+
+ it("#given an error with data as a string (not object) #when parsing #then does not crash", () => {
+ //#given
+ const error = {
+ data: "some-string-data",
+ message: "token limit exceeded",
+ }
+
+ //#when
+ const result = parseAnthropicTokenLimitError(error)
+
+ //#then
+ expect(result).not.toBeNull()
+ })
+})
diff --git a/src/hooks/anthropic-context-window-limit-recovery/parser.ts b/src/hooks/anthropic-context-window-limit-recovery/parser.ts
index dda87bbd5..451d1ab18 100644
--- a/src/hooks/anthropic-context-window-limit-recovery/parser.ts
+++ b/src/hooks/anthropic-context-window-limit-recovery/parser.ts
@@ -74,6 +74,14 @@ function isTokenLimitError(text: string): boolean {
}
export function parseAnthropicTokenLimitError(err: unknown): ParsedTokenLimitError | null {
+ try {
+ return parseAnthropicTokenLimitErrorUnsafe(err)
+ } catch {
+ return null
+ }
+}
+
+function parseAnthropicTokenLimitErrorUnsafe(err: unknown): ParsedTokenLimitError | null {
if (typeof err === "string") {
if (err.toLowerCase().includes("non-empty content")) {
return {