Signal delivered after a run's version is unregistered is consumed, leaving the run stranded in "running" (half-transition)
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 1-2 Tage
- Anfängerfreundlichkeit
- 52/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- postgresql, typescript
- Bereich
- backend, distributed-systems
Rechercherichtung
Start with the signal delivery path in packages/workflow-runtime/src/in-memory-store.ts and the version mismatch handling in packages/workflow-core/src/engine/run-workflow.ts; then read packages/workflow-runtime/src/runtime-driver.ts. Trace how delivery, mismatch handling, and lease release affect the stored run, using the described reproduction as a guide. Done means a mismatched version cannot leave the run running with no lease and no pending wait; confirm the selected outcome in the tests.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Versions
@tanstack/workflow-core 0.0.4, @tanstack/workflow-runtime 0.0.3. I see the same result on main at b928717, from a Postgres-store test suite rather than this exact snippet.
Describe the bug and the steps to reproduce it
The docs say to keep old versions in previousVersions until their runs terminate, so deploying v2 without v1 while a v1 run is paused is a deployment mistake. The bug is what that mistake does to the run: delivering a signal leaves it half-transitioned, and the public runtime API has no way to get it back.
Steps: pause a run on v1, deploy v2 without previousVersions: { v1 }, then deliver the signal through runtime.deliverSignal.
- The store accepts the delivery and clears the waiting state (in-memory-store.ts). The signal is consumed and the run moves from
pausedtorunning. - The core then finds no matching version, emits
RUN_ERRORED { code: 'workflow_version_mismatch' }and returns without saving anything (run-workflow.ts). - The runtime releases the lease (runtime-driver.ts).
The run ends up running, with no lease, no error and nothing awaited. Correcting the deploy doesn't help:
- Rolling back to a runtime that registers
v1and re-delivering returnsnot-waiting. - Stale-run recovery (#15) only claims runs whose lease has expired, so it never selects a run whose lease was released.
The docs promise a RUN_ERRORED event; they don't clearly say the run should be persisted as errored, so I'm not assuming that's the intended fix. Either outcome would be fine, as long as the run doesn't end up in this in-between state:
- (a) reject the delivery before it is consumed, leaving the run
paused, so fixing the deploy lets it resume. This seems safer for rolling deploys. - (b) persist a terminal
erroredstatus with the mismatch code, so the failure is visible and the run is no longerrunning.
Your Minimal, Reproducible Example
import { createWorkflow } from '@tanstack/workflow-core'
import { defineWorkflowRuntime, inMemoryWorkflowExecutionStore } from '@tanstack/workflow-runtime'
const def = (v: string) =>
createWorkflow({ id: 'wf', version: v }).handler(async (ctx) => {
const x = await ctx.waitForEvent<{ ok: boolean }>('go', { id: 'go' })
return x.ok
})
const store = inMemoryWorkflowExecutionStore()
const v1 = defineWorkflowRuntime({ store, workflows: { wf: { load: async () => def('v1'), version: 'v1' } } })
const v2 = defineWorkflowRuntime({ store, workflows: { wf: { load: async () => def('v2'), version: 'v2' } } })
await v1.startRun({ workflowId: 'wf', runId: 'r2', input: {} })
const res = await v2.deliverSignal({ runId: 'r2', stepId: 'go', name: 'go', signalId: 's1', payload: { ok: true } })
const run = await store.loadRun('r2')
console.log(res.kind, run?.status, run?.error, run?.lease) // 'running' 'running' undefined undefined
const retry = await v1.deliverSignal({ runId: 'r2', stepId: 'go', name: 'go', signalId: 's2', payload: { ok: true } })
console.log(retry.kind) // 'not-waiting': even after the deploy is fixed, the run can't continue
Do you intend to try to help solve this bug with your own PR?
Yes — happy to put up a PR once there's agreement on (a) vs (b).
Terms & Code of Conduct
- I agree to follow this project's Code of Conduct
- I understand that if my bug cannot be reliably reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
- Vorherrschende Sprache
- TypeScript
- Sterne
- 221
- Forks
- 10
- PR-Merge-Kennzahlen
- Keine gemergten PRs in 30 T.
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus TanStack/workflow
-
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 15/100
-
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 25/100
-
Dependency DashboardOffen
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 20/100
Alle Issues in TanStack/workflow
Ähnliche Issues
-
Shell: serve the home app's Cookie Policy on a hard reloadEvtl. vergeben @2001anshkaushik hat das heute übernommen. Offenfeature module:ai
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
rocketride-org/rocketride-server#2899 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 2 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
wazuh/wazuh-dashboard-plugins#9551 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
gofish-graphics/gofish-graphics#1252 ·
Maintainer antworten meist innerhalb von 1 Tag
-
optimization P3 size:XS
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 88/100
josephfung/curia#2366 ·
Maintainer antworten meist innerhalb von 1 Tag
-
type:bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
nightgauge/nightgauge#2688 ·
Maintainer antworten meist innerhalb von 1 Tag