Browse Source

fix(isRateLimitError): handle string-typed error values

OpenCode may send props.error as a plain string (e.g. "Usage exceeded")
in session.status events. isRateLimitError rejected these with its
typeof !== 'object' guard. Now checks strings directly against
RATE_LIMIT_PATTERNS before falling through to the object path.
Michael Henke 1 month ago
parent
commit
2072d9f2fa

+ 7 - 1
src/hooks/foreground-fallback/index.test.ts

@@ -126,8 +126,14 @@ describe('isRateLimitError', () => {
     expect(isRateLimitError(null)).toBe(false);
   });
 
+  test('returns true for string error with rate-limit message', () => {
+    expect(isRateLimitError('Usage exceeded')).toBe(true);
+    expect(isRateLimitError('rate limit exceeded')).toBe(true);
+    expect(isRateLimitError('quota exceeded')).toBe(true);
+  });
+
   test('returns false for non-object', () => {
-    expect(isRateLimitError('string error')).toBe(false);
+    expect(isRateLimitError(42)).toBe(false);
   });
 });
 

+ 6 - 1
src/hooks/foreground-fallback/index.ts

@@ -52,7 +52,12 @@ const RATE_LIMIT_PATTERNS = [
 ];
 
 export function isRateLimitError(error: unknown): boolean {
-  if (!error || typeof error !== 'object') return false;
+  if (!error) return false;
+  // Handle string-typed errors (OpenCode may send a plain error string)
+  if (typeof error === 'string') {
+    return RATE_LIMIT_PATTERNS.some((p) => p.test(error));
+  }
+  if (typeof error !== 'object') return false;
   const err = error as {
     message?: string;
     data?: { statusCode?: number; message?: string; responseBody?: string };