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
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.
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 429tells 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.tshas several places where the error is just the HTTP status with no recovery hint: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(allthrow new Error(...)andJSON.stringify({ error: ... })paths)Acceptance criteria
web_searchends with a hint clause (" — try: …" / " — next: …")web_fetch: rate-limit, HTTP non-2xx, timeout, non-http(s) URLnpm run test -- web)tests/web.test.tscovering the new error shapes (snapshot or.toContain('try:')is fine)Hints
ErrorWithHintclass.src/tools/shell.tsformatCommandResultfor an existing pattern of hint-style messages.Out of scope
Difficulty
2-3 hours.