Skip to content

[dataflow] overseer crashes with nil index when a :signal() timeout expires #109

Description

@rustamwin

The dataflow overseer process crashes with a nil-index error when a :signal() node's timeout expires. It restarts automatically after ~1s and I did not observe data loss, but since the overseer supervises all dataflow execution, a crash there seemed worth reporting.

Environment

  • wippyai/runtime built from source at commit 4da90cc (fix(s3): reject nil upload readers (#566), 2026-08-10)
  • wippy version reports dev unknown (source build, no ldflags), so I'm citing the commit instead
  • wippy/dataflow@0.7.12, wippy/llm@0.4.41, wippy/security@0.4.2, plus migration, session, views, bootloader, agent
  • go1.26.5, darwin/arm64

What happens

ERROR core  service userspace.dataflow.runner:overseer.service is failed
  {"serviceID": "userspace.dataflow.runner:overseer.service", "status": "failed",
   "error": "process failed: userspace.dataflow.runner:overseer:739:
             attempt to index a non-table object(nil) with key 'case_receive'"}

Reproducing

  1. Define a dataflow with a :signal() node carrying a timeout:
flow.create()
    :with_input(input):to("classify")
    :func("app.funcs:classify"):as("classify"):to("approve")
    :signal({
        signal_id = "approval",
        timeout = "30s",
        inputs = { required = { "classify" } }
    }):as("approve"):to("enrich")
    :func("app.funcs:enrich"):as("enrich"):to("@success")
    :start()
  1. Start it and never signal it.
  2. Let the timeout expire.

The crash reproduced on every boot where a timeout fired (I saw it across several runs, and it appeared consistently when more than one timeout expired close together — I did not isolate whether a single expiry alone is sufficient).

What I expected

The timeout itself works correctly and emits a well-formed result, which is why this looks like a bug in the surrounding code rather than in the feature:

{"code": "SIGNAL_TIMEOUT", "error": true, "timeout": true,
 "message": "Signal timed out: approval_timeout_direct_test",
 "timeout_ms": 30000, "timeout_deadline": "2026-08-11T01:51:37.195231+05:00"}

So the timeout path produces the right output and then the overseer falls over. I'd have expected the expiry to be handled without the supervising process dying.

Context

I was evaluating Wippy as a possible replacement for a Temporal-based workflow engine, and human-approval steps with a 24h timeout are a core use case for us — which is why this particular path got exercised hard. Happy to provide more logs or a fuller reproduction if useful.

One note in fairness: timeout on a signal node isn't documented (the Signal Node reference lists only signal_id / inputs / input_transform / metadata), so it's possible I'm using an unsupported parameter and this is expected. If so, the feature works well and might be worth documenting.

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