Repository navigation
pass on why a provider failed, in its words or with what to do next, and say when a web page was cut - #165
Merged
Conversation
…and say when a web page was cut Exa's CRAWL_* codes and Parallel's site statuses become not found, out of reach for now or turned down, each with the address and what to try next; unknown codes stay as before. An empty page fails instead of reading as the page, and a page cut at 100,000 characters ends with a note saying so. not_found and provider_unreachable take an optional message for agents: Granola's tool errors, any MCP tool or JSON-RPC error without a toolError of its own, and two answers where trying again would make a duplicate, a Notion page and an IMAP draft, now use it.
hkonsti
force-pushed
the
hkonsti/exa-page-errors
branch
from
October 30, 2026 20:24
97b2ca2 to
aa48b2f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
These fix problems agents reported with
send_feedbackwhen reading pages through Exa, and errors that dropped what a provider said. Over the last 10 days about 6% of Exa page reads failed, and every one was logged asvalidation_error. Agents were told "Exa turned it down" with nothing on whether to retry.Errors can carry a message.
not_foundandprovider_unreachabletake an optionalmessage. When there is one, agents get it word for word. That holds over MCP and in the web app, whereagentErrorMessagenow uses the api's words for 404 and 502. Without one, they read as before ("The provider could not be reached. Try again in a moment."). The activity log still keeps only the kind and HTTP status.Every page Exa couldn't read failed the same way. When Exa's MCP server can't read a page, it answers
Error fetching URL(s): <url>: <code>. Each code it gives now becomes an error with the address and what to do next:CRAWL_NOT_FOUND,CRAWL_HTTP_404,CRAWL_HTTP_410not_foundCRAWL_LIVECRAWL_TIMEOUTprovider_unreachableCRAWL_HTTP_429,CRAWL_HTTP_5xxprovider_unreachable, with the statusCRAWL_HTTP_401,CRAWL_HTTP_403validation_errorCRAWL_EMPTY_CONTENTvalidation_errorCRAWL_UNEXPECTED_CONTENT_TYPEvalidation_errorAnything else stays as it was. That includes
CRAWL_UNKNOWN_ERROR(which Exa gives for an address that doesn't resolve), other statuses andunknown error. Parallel'sweb_fetcherrors that come with a site status (404, 403, 429, 5xx) are mapped the same way. The messages live inconnections/page-failure.ts, which Exa, Parallel and the web connector all use.Empty pages were passed on as if they were the page. When Exa has nothing and no code for why, it answers "No content found for the provided URL(s)." without marking it as an error. That text used to come back as the page. Now it, and any page with no text from either provider, fails as above.
Long pages were cut without a word. Exa stops at
maxCharactersand doesn't say so, and we cut Parallel's full content at 100,000 characters just as quietly. A cut page now ends with a note, as Cloudflare's long answers do since #151:[Cut short: the page is 4,992,566 characters long, of which these are the first 100,000. Read a part of it at its own address, …]. Parallel returns the whole page, so the note gives its length. Exa is asked for 101,000 characters. When more than 100,000 come back, the page goes on, and the note says so without a length. The extra 1,000 cover whitespace that gets trimmed where Exa cuts the text.Other providers' errors.
toolError. Its tool errors, such as one about something the account's plan doesn't include, became a bare "could not be reached, try again", so agents kept retrying. Granola's words are now passed on as "Granola turned it down: …". The match is generic, since we haven't seen Granola's exact wording.provider_unreachablewhen the server has notoolError, or itstoolErrordoesn't recognise the error (Linear's and Notion's unrecognised failures). It now carries the tool's own text: "Linear answered with an error: …". A JSON-RPC error carries its message the same way. Tools write that text for the model, as any MCP client would show it. It goes to the agent only, never to the activity log.notion-create-pagesanswering without saying which page it made gets "look for it by its title before trying again". An IMAP server not saying which UID a saved draft got gets "look for it in the drafts before saving it again".Testing
bun run typecheck,bun run test,bun run lint(one existing warning inagent-collection-page.tsx)mcp-clientdefault for tool errors and JSON-RPC errors, with and without wordssrc/tools/webFetch.ts) for its tool parameters and error format.mcp.exa.ai/mcpthroughcallMcpToolwith the newEXA.toolError, using test addresses that returned 404, 403, 429, 503, a slow response, an image and an empty body. Each one mapped as in the table. An address that doesn't resolve stays as before. A long spec page came back at exactly 101,000 characters, so the cut is detected. Parallel's keylessweb_fetchgavehttp_errorwith 404 and 403, and returned a 5-million-character page whole.