Skip to content

REST status mapping reads error_reason (prose) instead of error (code) — every tool failure returns 500 #262

Description

@divya0795

Describe the bug

statusForStageResult() in src/daemon/rest/errors.ts looks up its status tables against
f.error_reason, but tools put the machine code in f.error and a human sentence in
f.error_reason. None of the three lookup sets ever match, so the function falls through to its
default and every tool failure returns HTTP 500 — via /v1/* (dispatch.ts) and via the
Firecrawl-compat shim (firecrawl-compat.ts).

The two field names carry opposite meanings in two different shapes, which is what makes this easy
to miss:

shape error error_reason
StageError (tool outputs — src/types.ts:9) machine code (invalid_url, fetch_failed, http_404) human prose
ErrorEnvelope (REST — errors.ts errorEnvelope(error_reason, error, …)) human prose machine code

statusForStageResult takes a StageFailure (the tool shape) but reads it with the envelope
convention.

Producers, for reference — src/tools/fetch.ts:

// :186   { ok: false, error: 'invalid_url',  error_reason: urlValidation.reason, stage: 'fetch' }
// :288   { ok: false, error: `http_${raw.statusCode}`, error_reason: `Upstream returned HTTP …` }
// :414   { ok: false, error: 'fetch_failed', error_reason: described.reason, stage: 'fetch' }

A second, smaller problem in the same function: FETCH_UPSTREAM_REASONS contains 'http_error',
but fetch.ts:288 emits http_404, http_503, … so upstream HTTP failures would not map to 502
even after the field is corrected.

To reproduce

wigolo serve --port 8787
curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:8787/v1/fetch \
  -H 'content-type: application/json' -d '{"url":"not a url"}'

Or, as a pure unit-level repro:

statusForStageResult({ error: 'invalid_url', error_reason: 'url is not a valid absolute URL', stage: 'fetch' })

Expected behavior

failure expected actual
invalid_url (bad input) 400 500
fetch_failed / blocked_by_challenge (upstream) 502 500
http_404 (upstream HTTP) 502 500
browser_engine_unavailable 503 500

Output / logs

500 for all of the above. The response body is correct — errorEnvelope() maps the fields
properly at dispatch.ts:51; only the status line is wrong. So a client that reads the body sees
invalid_url while the status says "server error", and any client or proxy that retries on 5xx will
retry requests that can never succeed.

Environment

  • wigolo version: main @ 56da0c8
  • Node.js version: 20
  • OS: any
  • Which tool/command: wigolo serve/v1/fetch, /v1/search, /v1/extract, and the
    /v1/scrape Firecrawl-compat routes

Additional context

The existing unit tests pass because they were written with the code in error_reason, mirroring the
bug — e.g. tests/unit/daemon/rest-errors.test.ts:91 and tests/unit/daemon/rest-dispatch.test.ts:60
({ error: 'blocked', error_reason: 'blocked_by_challenge' }), neither of which is a shape any tool
actually produces.

Happy to open a PR: read f.error in the three lookups, add an anchored ^http_\d{3}$ case for the
502 branch, and re-point those tests at real producer-shaped failures.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions