Skip to content

Make web_search / web_fetch errors actionable #16

@esengine

Description

@esengine

Background

Tool errors in Reasonix go back to the model verbatim — they're how the agent decides what to do next. A bare web_search 429 tells the model "something failed" but not "back off and retry" vs "the URL was wrong" vs "the engine is blocked"; the model often just retries identically and burns tokens. Better error strings → fewer wasted turns.

Current behaviour

src/tools/web.ts has several places where the error is just the HTTP status with no recovery hint:

// src/tools/web.ts:58
if (!resp.ok) throw new Error(`web_search ${resp.status}`);

// src/tools/web.ts:130
if (!resp.ok) throw new Error(`web_fetch ${resp.status} for ${url}`);

The 429 / 403 anti-bot path (line 64) does include a hint, but most others don't.

Expected behaviour

Every error includes a short " — try: …" or " — next: …" tail telling the model what action would unstick it.

Examples:

  • web_search 429 — try: wait 10s before retrying, or rephrase the query (rate-limited)
  • web_fetch ${status} for ${url} — try: confirm the URL resolves in a browser; status suggests the host returned an error page (HTTP error)
  • web_fetch: timed out after ${ms}ms — try: shorter URL or smaller content; this may be a slow CDN (timeout)
  • web_fetch: url must start with http:// or https:// already exists at line 274 — leave it; it's the model that.

Files to touch

  • src/tools/web.ts (all throw new Error(...) and JSON.stringify({ error: ... }) paths)

Acceptance criteria

  • Every error message produced by web_search ends with a hint clause (" — try: …" / " — next: …")
  • At least three distinct hints: rate-limit (429), forbidden (403), generic 5xx
  • Same treatment for web_fetch: rate-limit, HTTP non-2xx, timeout, non-http(s) URL
  • Existing tests still pass (npm run test -- web)
  • Add 2-3 new test cases in tests/web.test.ts covering the new error shapes (snapshot or .toContain('try:') is fine)

Hints

  • Don't over-engineer. The hints are short strings; resist the urge to introduce an ErrorWithHint class.
  • Focus on what a language model would find useful. Phrases like "the URL is malformed" beat "ERR_INVALID_URL".
  • Look at src/tools/shell.ts formatCommandResult for an existing pattern of hint-style messages.

Out of scope

  • Refactoring the search adapter selection (Mojeek vs Tavily)
  • Adding retry logic — that's the model's job
  • Internationalising the messages (everything stays English)

Difficulty

2-3 hours.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions