The rule, written down in README "Taking an event", the on() doc and stdio.Subscribe's doc: a handler takes an event by returning and asks for it again by throwing (in Go, returning an error). On the wire, any mesh/event answer that is not a JSON-RPC error takes the event. An error, no answer within the event timeout, or the bundle exiting gets it offered again after 5s, up to max-deliveries, after which the controller raises max-deliveries. So a handler throws only when the work was not done, and its work must be safe to repeat, or it skips an event.id it already handled. This came out of plex on ace, which threw after a successful rescan because it parsed an empty 200 body.
Fix, found on the way: both SDKs added the handler before mesh/subscribe was answered and kept it when the runtime refused. A module that keeps asking until the runtime takes it (every Go consumer does this) then ran the same handler once per attempt for every event. Now:
TS removes the handler when the subscribe is refused.
Go removes it when the runtime answered with an error. If the runtime did not answer in time, Go keeps the handler, because the runtime may have bound it.
Tests: a new TS stdio test with fixture stdio-resubscribe.mjs, and Go TestARefusedSubscribeLeavesNoHandlerBehind. Both fail without the fix and pass with it. All TS stdio tests pass, and go test ./... passes.
Rollout: the package version goes to 0.1.13, and the pipeline publishes it. Go consumers (postgres, records, messenger, mesh-watcher) pick up the fix only after a go/v0.1.13 tag and a bump of their SDK version. I did not create the tag.
**The rule, written down in README "Taking an event", the `on()` doc and `stdio.Subscribe`'s doc:** a handler takes an event by returning and asks for it again by throwing (in Go, returning an error). On the wire, any `mesh/event` answer that is not a JSON-RPC error takes the event. An error, no answer within the event timeout, or the bundle exiting gets it offered again after 5s, up to max-deliveries, after which the controller raises `max-deliveries`. So a handler throws only when the work was not done, and its work must be safe to repeat, or it skips an `event.id` it already handled. This came out of plex on ace, which threw after a successful rescan because it parsed an empty 200 body.
**Fix, found on the way:** both SDKs added the handler before `mesh/subscribe` was answered and kept it when the runtime refused. A module that keeps asking until the runtime takes it (every Go consumer does this) then ran the same handler once per attempt for every event. Now:
- TS removes the handler when the subscribe is refused.
- Go removes it when the runtime answered with an error. If the runtime did not answer in time, Go keeps the handler, because the runtime may have bound it.
**Tests:** a new TS stdio test with fixture `stdio-resubscribe.mjs`, and Go `TestARefusedSubscribeLeavesNoHandlerBehind`. Both fail without the fix and pass with it. All TS stdio tests pass, and `go test ./...` passes.
**Rollout:** the package version goes to 0.1.13, and the pipeline publishes it. Go consumers (postgres, records, messenger, mesh-watcher) pick up the fix only after a `go/v0.1.13` tag and a bump of their SDK version. I did not create the tag.
Related: novox/mesh-media-catalog#5, novox/mesh-tools, novox/hq issue 276.
A handler takes an event by returning and asks for it again by throwing;
any mesh/event answer but an error is taken. Written down in the README
where event handling is, with what follows for a handler: throw only when
the work was not done, and be safe to run twice.
Both SDKs added the handler before asking the runtime and kept it when the
runtime refused, so a module that asks again until it is taken (every Go
consumer does) ran its handler once per attempt for every event. A refused
subscribe now removes its handler; one the Go runtime did not answer in
time is kept, since the runtime may have bound it.
Deliverynovox/mesh-sdk@9f875b3d5c2f — delivered since 2026-10-06T23:05:28Z
Transitions
2026-10-06 23:05 (new) → proposed (announced): novox/mesh-sdk#10 merged; its head was not heard by this owner, so it is made from the forge's word
2026-10-06 23:05 proposed → held (merged-unchecked): merged without a passing check — a verdict known with it is taken first: only a person decides that it goes on
2026-10-06 23:05 held → delivering (go): its walk started on a word that was not this owner's: the controller's own path, which waits for nobody, or a person's plans go
2026-10-06 23:05 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says
The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.
<!-- mesh-delivery:view -->
**Delivery** `novox/mesh-sdk@9f875b3d5c2f` — **delivered** since 2026-10-06T23:05:28Z
**Transitions**
- 2026-10-06 23:05 (new) → proposed (announced): novox/mesh-sdk#10 merged; its head was not heard by this owner, so it is made from the forge's word
- 2026-10-06 23:05 proposed → held (merged-unchecked): merged without a passing check — a verdict known with it is taken first: only a person decides that it goes on
- 2026-10-06 23:05 held → delivering (go): its walk started on a word that was not this owner's: the controller's own path, which waits for nobody, or a person's `plans go`
- 2026-10-06 23:05 delivering → delivered (done): its walk is done: every machine of its deploy plan passed, or was left as its policy says
The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The rule, written down in README "Taking an event", the
on()doc andstdio.Subscribe's doc: a handler takes an event by returning and asks for it again by throwing (in Go, returning an error). On the wire, anymesh/eventanswer that is not a JSON-RPC error takes the event. An error, no answer within the event timeout, or the bundle exiting gets it offered again after 5s, up to max-deliveries, after which the controller raisesmax-deliveries. So a handler throws only when the work was not done, and its work must be safe to repeat, or it skips anevent.idit already handled. This came out of plex on ace, which threw after a successful rescan because it parsed an empty 200 body.Fix, found on the way: both SDKs added the handler before
mesh/subscribewas answered and kept it when the runtime refused. A module that keeps asking until the runtime takes it (every Go consumer does this) then ran the same handler once per attempt for every event. Now:Tests: a new TS stdio test with fixture
stdio-resubscribe.mjs, and GoTestARefusedSubscribeLeavesNoHandlerBehind. Both fail without the fix and pass with it. All TS stdio tests pass, andgo test ./...passes.Rollout: the package version goes to 0.1.13, and the pipeline publishes it. Go consumers (postgres, records, messenger, mesh-watcher) pick up the fix only after a
go/v0.1.13tag and a bump of their SDK version. I did not create the tag.Related: novox/mesh-media-catalog#5, novox/mesh-tools, novox/hq issue 276.
Delivery
novox/mesh-sdk@9f875b3d5c2f— delivered since 2026-10-06T23:05:28ZTransitions
plans goThe commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.