Signal delivered after a run's version is unregistered is consumed, leaving the run stranded in "running" (half-transition)
还没有人认领这个 Issue。
评估
- 难度
- 4/5
- 预计耗时
- 1-2 天
- 新手友好度
- 52/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- postgresql, typescript
调研方向
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.
由索引模型根据 Issue 内容生成。
描述
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.
- 主要语言
- TypeScript
- 星标
- 221
- 派生
- 10
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
TanStack/workflow 的其他 Issue
-
难度 1/5 1 小时以内 新手友好度 15/100
-
难度 5/5 一周以上 新手友好度 25/100
-
难度 5/5 一周以上 新手友好度 20/100
查看 TanStack/workflow 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 78/100
gnosis/gnosis_vpn#661 ·
维护者通常 1 天内回复
-
content-freshness: staleOwnedContent doc comments still say 'complete receipt' (follow-up to #7912)未关闭triage:bug-verified
难度 1/5 1 小时以内 新手友好度 92/100
维护者通常 1 天内回复
-
fix: reject sourceStorage/packagedStorage bucket that differs from the stack default (fail loud)未关闭
难度 2/5 1-3 小时 新手友好度 82/100
Eyevinn/open-videocore#1300 ·
维护者通常 1 天内回复
-
nextjs-tracking
难度 2/5 1-3 小时 新手友好度 67/100
cloudflare/vinext#4390 ·
维护者通常 1 天内回复
-
kind/bug
难度 2/5 1-3 小时 新手友好度 82/100
kubernetes-sigs/headlamp#8175 ·
维护者通常 2 天内回复