Skip to content

[exa-js]: robust error handling with retries for transient failures#140

Open
devin-ai-integration[bot] wants to merge 1 commit intomasterfrom
devin/1771443249-robust-error-handling
Open

[exa-js]: robust error handling with retries for transient failures#140
devin-ai-integration[bot] wants to merge 1 commit intomasterfrom
devin/1771443249-robust-error-handling

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

[exa-js]: robust error handling with retries for transient failures

Summary

Addresses customer-reported SyntaxError: Unexpected token '<' and TypeError: fetch failed errors on websets API calls (websets.get, websets.create, websets.items, websets.searches.create).

The root cause: the request() method in src/index.ts had no protection against:

  1. HTML error responses — when a load balancer returns a 502/503 HTML page, response.json() throws SyntaxError instead of producing a useful ExaError
  2. Network failuresfetch() throwing TypeError: fetch failed propagated raw instead of as ExaError

Changes to the request() method:

  • Wrap fetchImpl() in try/catch → network errors become ExaError with statusCode: 0
  • Wrap response.json() on both error and success paths → HTML/non-JSON bodies produce descriptive ExaError with truncated body preview
  • Add retry loop (up to 2 retries, exponential backoff starting at 1s) for transient status codes (429, 502, 503, 504) and network errors

Review & Testing Checklist for Human

  • Retrying non-idempotent requests: The retry logic applies to ALL HTTP methods including POST. websets.create and websets.searches.create go through this path — retrying these on 502/503 could create duplicate resources. Consider whether retries should be limited to GET/idempotent methods, or if the websets API is idempotent by design.
  • Double body consumption: When response.json() fails, the catch block calls response.text(). After .json() partially consumes the body stream, .text() may return empty string (handled by .catch(() => "")) — so error messages may say Non-JSON response () with no body preview. Verify this is acceptable or consider reading body as text first, then parsing.
  • No unit tests for new retry/error paths: The 74 existing tests pass but none exercise retry behavior, HTML response handling, or network error wrapping. Consider adding tests before shipping.
  • rawRequest() not updated: Only request() gets retries/error wrapping. rawRequest() returns raw Response and is unchanged — verify no callers depend on it having similar resilience.
  • Retry config not user-configurable: DEFAULT_MAX_RETRIES=2 and INITIAL_RETRY_DELAY_MS=1000 are module-level constants. Users cannot disable or tune retries. Consider if this needs to be configurable via constructor options.

Notes

  • Pre-existing type errors in test files (20 errors across 3 test files) are unrelated to this change
  • Build and all 74 unit tests pass

Link to Devin run: https://app.devin.ai/sessions/a7d384b145ae4fa58ee98bf15d751535
Requested by: Yi Yang

…ilures

- Wrap fetchImpl in try/catch to convert network errors (TypeError: fetch failed) into ExaError
- Safely parse JSON on error path: when server returns HTML (e.g. 502 load balancer page), catch SyntaxError and include truncated response body in error message
- Safely parse JSON on success path for the same reason
- Add automatic retry with exponential backoff for transient failures (429, 502, 503, 504) and network errors (up to 2 retries)

Co-Authored-By: Yi Yang <yi@exa.ai>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants