Skip to content

Result

Result is the dataclass returned by wf.run(), wf.run_async(), and wf.deploy_and_run(). It captures everything you need to know about a single workflow execution:

from athena_sdk import Workflow

wf = Workflow("demo")
wf.python_transform(
    "echo",
    code="def transform(row):\n    return {'ok': True}\n",
)
result = wf.run()

print(result.ok)                            # True / False
print(result.status)                        # "completed" | "failed"
print(result.duration)                      # wall-clock seconds
print(result.execution_id)                  # backend run id (None for pure-local)
print(result.node_results["echo"].output)

Truthy shortcut

Result.__bool__ is True when the workflow completed successfully and False otherwise, so you can treat the result like a status sentinel:

if not wf.run():
    raise RuntimeError("workflow failed — see logs")

result.ok is the same check spelled out explicitly. Use whichever reads better at the call site.

Per-node outcomes

Each node that ran is in result.node_results, keyed by node name:

@dataclass
class NodeResult:
    name: str
    status: str               # "completed" | "failed" | "skipped"
    duration: float | None    # seconds, may be None for skipped nodes
    output: Any               # whatever the node returned
    error: str | None         # populated when status == "failed"

A typical post-mortem loop:

result = wf.run()
for name, nr in result.node_results.items():
    if nr.status == "failed":
        print(f"✗ {name} ({nr.duration:.2f}s): {nr.error}")
    elif nr.status == "skipped":
        print(f"– {name} skipped")
    else:
        print(f"✓ {name} ({nr.duration:.2f}s)")

Skipped nodes appear when an upstream if_ / switch routed control away from them, or when wf.deploy_and_run() is invoked with partial=True against a hosted backend.

Outputs

NodeResult.output is whatever the node produced — a list of dicts from a Postgres SELECT, a single dict from a python_transform, an S3 object key from an S3 write, etc. The shape is documented per-node in the API reference. For typed access across nodes inside a workflow, use expressions:

from athena_sdk import expr

wf.api(
    "notify",
    url="https://hooks.slack.com/...",
    body={"text": expr.node("Loader").get("rows").len()},
)

Logs

result.logs is a list of human-readable log lines emitted during the run. The bundled engine captures stdout/stderr from python_transform nodes plus its own diagnostic messages here. For larger or streaming use cases (long-running workflows on a hosted backend), see AthenaClient.executions.logs_stream().

Errors

When status == "failed", result.error is a high-level summary string and the failing node will have its own error populated. For typed exception handling on the build side (before you ever call run()), see Errors:

Exception Raised when
WorkflowBuildError A typed builder rejects an invalid configuration at build time
ValidationError A hosted backend returns 422 from client.workflows.create()
RemoteAPIError Any non-2xx from a hosted backend (network, auth, server)
AthenaSDKError Catch-all base for all SDK exceptions

Local execution failures surface through the Result object, not exceptions — wf.run() only raises if something failed catastrophically before any node could run (e.g. the engine couldn't connect to its state Postgres).