Skip to content

automation: both trigger routes still answer HTTP 200 wrapping an inner {success:false} — the same #3962 residue #8684 closed on resume #9378

Description

@os-zhuang

Found while implementing #8684 (resume-route status unification). Filed rather than fixed there: same defect class, different engine path, and materially wider blast radius.

Measured

origin/main @ d6e793507, packages/runtime/src/domains/automation.ts. Both trigger doors end the same way:

  • POST /api/v1/automation/trigger/:name (legacy shape, the one client.automation.trigger() calls) — :570-576
  • POST /api/v1/automation/:name/trigger:742-747
constresult=awaitautomationService.execute(name,buildAutomationContext(body,context));return{handled: true,response: deps.success(result)};

deps.success(result) is unconditional, so a flow that RAN and FAILED comes back as HTTP 200 wrapping {"success":true,"data":{"success":false,"error":"…"}} — the exact double envelope #3962 ruled out for /actions and #8684 has now closed on the resume route. A caller that branches on the HTTP status alone reads a failed run as a successful one.

Why it was not fixed inside #8684

#8684's fix is producer-first: the engine classifies, the route maps. On the resume path that was mechanical — the two never-dispatched exits are unambiguous, and the ruling had already fixed the two rows. execute() is a different path with its own code-less exits, and classifying them is a design decision, not the mechanical application of #8684:

engine.ts exitrealityplausible answer
:3043Flow '<name>' not foundnever dispatched404
:3048Flow '<name>' is disabledrefused, deliberately409? 404? 403? — needs a ruling
:3121Flow has no start nodemalformed definition422? 500?
:3302 ran and failed (incl. the retry-strategy exits :6088 / :6221)ran, rejected400 FLOW_FAILED

It is also a much wider break than the resume half: the trigger route is the primary flow-run entry point, so every client.automation.execute() / .trigger() caller changes from "resolves with an inner failure" to "rejects" (the SDK's _fetch throws on non-2xx). #8684 touched one route used by the screen-flow runner; this touches the surface every app dispatches flows through.

Prior art to inherit, not re-derive

Not decided here

Whether the disabled-flow and malformed-definition exits are refusals or failures, and whether the trigger flip ships in the same release as #8684's. Both want a ruling before implementation.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions