execute runs a deployed workflow definition. It resolves a session, creates one execution against it, prints
the debugger URL, and — with --watch — tails the execution’s events until it reaches a terminal status.
It is the command that closes the loop after deploy: the same manifest that
says what to deploy also says what to run, so a CI job can deploy a revision and exercise it without knowing an
id.
Synopsis
--definition behaves differently here than anywhere else in the group: it is required and takes one
alias, not a repeatable filter. One invocation is one execution.
What it does
- Resolves the blob and revision. This is a read — a
commit-phase revision executes fine. - Lists the revision’s definitions and matches the manifest, refusing an alias the manifest does not carry with
DEFINITION_NOT_ACCESSIBLE. - Refuses a
playgroundentry withDEFINITION_NOT_EXECUTABLE, before creating a session or an execution. - Resolves
--sessionto an id, creating the session if the reference is a new alias. - Creates the execution and builds its debugger URL.
- With
--watch, polls the execution’s events until a terminal status; with--open, launches a browser.
--json the payload carries execution_id, session_id, session_created, definition, status and
url, alongside the envelope’s schema_version. The payload is emitted even when the run fails — the result
is printed before EXECUTION_FAILED is raised, so a --json consumer still gets the execution id it needs to go
look at what happened.
--open launches a browser only when --json is off. The URL is in the payload either way, so a wrapper can
open it itself; a subprocess that opens a browser behind a machine consumer’s back is not a side effect the CLI
takes on its own.
How --session is resolved
Every workflow execution belongs to a session. Omit --session and one is created for this run.
When you do pass a reference, the CLI resolves it client-side, and this is not an implementation detail — it
is forced by an asymmetry in the platform. create_execution accepts a session id only, while get_session
accepts an id or an alias. So execute calls get_session with whatever you passed and hands only the
resolved id onward. The alias itself is never sent.
What happens when the reference does not resolve depends on its shape, because the platform reports absent and
not yours as the same 403:
A session that resolves but is not open — closed, closing, or deleting — is refused with
SESSION_NOT_OPEN
before the execution is attempted. The CLI will not reopen it for you. Reporting it here, where you can still
act, is the point: the alternative is an opaque conflict out of create_execution after the fact.
The definition is addressed by id, never by alias
The platform can look a definition up by alias, andexecute deliberately does not use that path. The server’s
alias lookup applies no category filter, while alias uniqueness is scoped per (category, alias) — so one
alias may legally name both a workflow and a playground definition on the same revision, and an alias-addressed
execution would resolve to whichever the query happened to return first.
execute already holds the definition’s id from the listing it did in step 2, so this ambiguity never reaches
the platform.
The same shared alias is still worth knowing about locally. --definition takes a bare alias with no
category qualifier, so on a manifest that declares one alias in both categories, execute takes the first
matching entry in manifest order and does not report the collision. If that entry is the playground one, the run
stops at DEFINITION_NOT_EXECUTABLE even though a workflow definition of that name exists. Use distinct aliases,
or declare the workflow entry first. ls shows both, with their categories.
--watch polls, and that is exact
--watch follows the execution by polling its event listing every two seconds, remembering the last event’s
created_at and asking for everything after it. It does not open a WebSocket.
That is not a compromise. The platform’s created_since filter is strictly exclusive, so a poll that resumes
from the last event it saw redelivers nothing and skips nothing — the tail is exact, not merely close enough.
When the status goes terminal, the events are drained once more before returning, so the final lines of a fast
workflow are not lost between the last page and the status read.
Events print one per line as they arrive, as the event’s type and message joined by a colon. An event with no
message prints its type alone:
- A terminal
failedstatus raisesEXECUTION_FAILEDafter the result is printed. The events above the error are the platform’s own account of what went wrong. - Fifteen minutes without a terminal status raises the advisory
EXECUTION_WATCH_TIMEOUTand stops tailing. The execution is still running on the platform and is unaffected — only the tail gave up. The status in the payload is the last one actually observed, not an empty value.
--watch the command returns as soon as the execution is created, and the reported status is whatever
the platform assigned at creation — typically creating. That is the right shape for fire-and-forget; it is not
a claim that the run succeeded.
Errors
EXECUTION_FAILED is the one code here that means the platform did its job: the definition ran and the run
failed. Everything above it means the run never started.
Every code above, with its remediation, is in Error codes.
See also
blobhub workflow deploy— what puts the definition on the revision in the first place.blobhub workflow ls— every definition on the revision, with its category.blobhub workflow check— static validation, which is a different question from whether a run succeeds.- Session lifecycle — what an execution belongs to, and what
openmeans. - Execution events — the stream
--watchprints.

