Skip to content

🪲 BUG: DotFlow ignores WORKFLOW_ID env var in managed runs #264

@FernandoCelmer

Description

@FernandoCelmer

Summary

When dotflow start runs in an environment that sets the WORKFLOW_ID variable, DotFlow() ignores it and generates a fresh random UUID anyway. This breaks any integration that expects the runner to adopt a pre-existing workflow id supplied externally (e.g. a managed server that already created the workflow record and scoped an access token to that specific id).

Impact

  • Severity: Blocker for every managed integration.
  • Scope: Every dotflow start invocation where the caller relies on WORKFLOW_ID being honoured.
  • Symptom: HTTP callbacks from ServerDefault target a brand-new UUID that does not match the token's scope, so the server rejects every call with 401/403. The workflow runs locally but all task status updates are lost.

Reproduction

Case A — env var ignored

export WORKFLOW_ID=f6f6ca26-b223-4f60-9552-ded50435ac5c
export SERVER_BASE_URL=https://example.com/api/v1
export SERVER_USER_TOKEN=<token scoped to f6f6ca26-...>

dotflow start --step docs_src.basic.simple_cli:simple_step

Runner logs:

POST   /api/v1/workflows                            [401]   # new random UUID registered
POST   /api/v1/workflows/f6f6ca26-.../tasks         [403]   # path uses env, body uses new UUID
PATCH  /api/v1/workflows/f6f6ca26-.../tasks/01K...  [403]
PATCH  /api/v1/workflows/f6f6ca26-...               [403]

Case B — expected behaviour (currently only works via explicit arg)

from dotflow import DotFlow

workflow = DotFlow(workflow_id="f6f6ca26-b223-4f60-9552-ded50435ac5c")
workflow.task.add(step=my_step)
workflow.start()

This path already skips create_workflow because _externally_provided_id is True. The env-var path should behave the same way.

Root Cause

Two code paths combine to produce the bug.

1. dotflow/core/dotflow.py:51

class DotFlow:
    def __init__(self, config=None, workflow_id=None):
        self._externally_provided_id = workflow_id is not None
        self.workflow_id = workflow_id or uuid4()   # <- env var is ignored

2. dotflow/cli/commands/start.py:63

def _new_workflow(self):
    storage = self._build_storage()
    if storage is None:
        return DotFlow()                            # <- no workflow_id arg
    return DotFlow(config=Config(storage=storage))  # <- same

The CLI never forwards an id and the constructor never reads the env, so the runner invents a fresh UUID on every launch. ServerDefault (dotflow/providers/server_default.py) then:

  1. Calls POST /workflows with the new UUID — no external system expects this, because the caller believes the workflow already exists with the id stored in WORKFLOW_ID.
  2. Reports task updates under that new UUID, which every scoped-token integration rejects.

Expected Behaviour

When WORKFLOW_ID is set in the environment, the runner must adopt it as its workflow id and skip the POST /workflows registration call — the same path already taken when workflow_id is passed explicitly to DotFlow(...).

Standalone runs (no env var, no explicit arg) must keep the current behaviour: generate a fresh UUID and register the workflow.

Proposed Fix

In dotflow/core/dotflow.py, fall back to the env var before uuid4():

import os

def __init__(self, config=None, workflow_id=None):
    workflow_id = workflow_id or os.getenv("WORKFLOW_ID")
    self._externally_provided_id = workflow_id is not None
    self.workflow_id = workflow_id or uuid4()
    ...

This:

  1. Honours WORKFLOW_ID when no explicit id is passed.
  2. Flips _externally_provided_id to True, so create_workflow is skipped (fixes the 401).
  3. Uses the same id the external system expects, so all subsequent calls line up (fixes the 403s).
  4. Does not affect standalone runs — if the env is unset, os.getenv returns None and the code falls through to uuid4() exactly as today.

Out of Scope

  • Adding an explicit --workflow-id CLI flag — tracked separately if needed.
  • Any changes to remote server behaviour. The contract is: if you supply WORKFLOW_ID, the runner trusts that the id already exists.

Acceptance Criteria

  • DotFlow() with no args and WORKFLOW_ID=abc in env yields workflow_id == "abc".
  • DotFlow() with no args and no env var generates a new UUID (current behaviour).
  • DotFlow(workflow_id="xyz") always wins, regardless of env.
  • When WORKFLOW_ID is set, server.create_workflow is NOT called.
  • New unit tests cover the three cases above.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions