fix(agent-bff): report an agent timeout as 504 agent_timeout - #1864
fix(agent-bff): report an agent timeout as 504 agent_timeout#1864Tonours wants to merge 9 commits into
Conversation
1 new issue
|
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (2)
🛟 Help
|
There was a problem hiding this comment.
Spec (PRD-1108): conforms. A slow agent now answers 504 agent_timeout (real-transport regression test), a refused connection still answers 502 network_error with The agent could not be reached, and the OpenAPI document declares 504 on every data and action operation through the shared Error504 component, distinct from Error504AiQuery.
Applies to: the PR as a whole.
[Preferential] This PR embeds a decision that meets the ADR bar: it is hard to reverse (the agent_timeout type is a public contract that cross-repo consumers branch on, forest-for-zendesk must learn it), surprising without context (timeout detection keys on superagent's raw error shape, code === 'ECONNABORTED' plus a numeric timeout, inside the BFF mapper), and a real trade-off (coupling to superagent's shape, guarded by the transport regression test, versus throwing a typed error from the separately-published agent-client package). No ADR in the org covers it (docs/adr search on timeout, error, openapi, bff, http, status: no relevant match). Consider recording it with /adr so the rationale survives outside this PR's plan.
nbouliol
left a comment
There was a problem hiding this comment.
Two follow-ups outside the diff: the README env table documents BFF_AI_TIMEOUT_MS and its 504 in detail but has no row for BFF_AGENT_TIMEOUT_MS, and the error-contract section doesn't mention agent_timeout. And per the ticket's Zendesk scope, forest-client.ts will bucket agent_timeout as unknown/non-retryable until a matching change ships.
|
Both fixed on this push.
The error-contract paragraph in Request edge now names the split:
On the Zendesk side: agreed, |
|
Third push. The review pass caught that my own correction was itself overstated. The blackhole → 504 claim needed a bound. It holds only while Both the OpenAPI 504 description and the README now say "at the default", and name where it stops holding. A test asserts that clause, so the qualification cannot be dropped silently. Dropped the Fixed a garbled README clause ("…answers Two commit titles on this branch are 73–74 chars. The repo squashes on the PR title, so neither reaches |
nbouliol
left a comment
There was a problem hiding this comment.
The typed AgentTimeoutError at the transport seam is the right fix — the mapper no longer knows superagent exists, and the 502/504 split is now documented in the OpenAPI descriptions and the README. Both regression cases are pinned end-to-end against real sockets.
One thing worth a follow-up: the narrowed 502 description now names only refused-connection and DNS, but that branch is still the catch-all for ECONNRESET, socket hang up and TLS failures.
be487b8 to
63f2f3e
Compare

What
An upstream agent timeout answers
504 agent_timeoutinstead of502 network_error. Every data and action operation of the OpenAPI document declares504through a sharedError504component, distinct from the AI relay'sError504AiQuery.502 network_errornow means what it says: the transport failed outright — connection refused, host unresolvable.fixes PRD-1108
Why / decisions
agent_timeoutrather than reusing the AI relay'supstream_timeout, which names the Forest server hop, not the agent.create-agent-http-requester.tstranslates superagent'scode === 'ECONNABORTED'plus numerictimeoutinto an exportedAgentTimeoutError;mapAgentErrorbranches oninstanceof, like it already does forAgentHttpError. The BFF imports superagent nowhere else, and the mapper had no business knowing its private error shape.queryandstreamare wrapped unconditionally. The requester used to return the bareHttpRequesterwhen no default timeout was configured, so a caller passing its ownmaxTimeAllowedgot an untranslated superagent error and a502.HttpRequesteragainst a server that accepts and never answers within a shortmaxTimeAllowed, so the 504 path stays bound to superagent's real error shape. If that shape changes, the test fails.502 vs 504, precisely
The deadline is armed when the request starts, and the default is 10 s (
BFF_AGENT_TIMEOUT_MS). So:BFF_AGENT_TIMEOUT_MSpast it and that case reverts to 502: the kernel gives up first and reportsETIMEDOUT, which superagent passes through raw.Both descriptions say this now, and the README carries it in the error-contract paragraph.
Scope and safety
504 upstream_timeoutthrough its ownfetch+AbortSignal.timeoutpath, untouched.HttpRequester.streamstill never settles on a timeout. Pre-existing, no BFF call site uses it, left as a known defect for agent-client.502 network_errorfor slow agents now sees504 agent_timeout. The Zendesk client is the known consumer, tracked in the ticket's satellite scope — and a timeout is exactly the case that should be retryable there, so it is a real behaviour gap rather than a cosmetic one.Documented
BFF_AGENT_TIMEOUT_MShad no row in the README env table despiteBFF_AI_TIMEOUT_MShaving a detailed one. Both the row and theagent_timeout/network_errorsplit are in now.How to test
yarn workspace @forestadmin/agent-bff testyarn workspace @forestadmin/agent-bff lint && yarn workspace @forestadmin/agent-bff buildDefinition of Done
General
Security