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
const result = await automationService.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 exit reality plausible answer
:3043 Flow '<name>' not found never dispatched 404
:3048 Flow '<name>' is disabled refused, deliberately 409? 404? 403? — needs a ruling
:3121 Flow has no start node malformed definition 422? 500?
:3302 ran and failed (incl. the retry-strategy exits :6088 / :6221) ran, rejected 400 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

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions