On ace, [mesh-tools] plex did not take radarr.download.completed: Unexpected end of JSON input; offered again looked like the runtime failing to parse the bundle's answer. It wasn't. The runtime parses nothing in a mesh/event answer. That text was plex's own handler error (JSON.parse of an empty body), passed through by the SDK.
Change (minimal):
launch.Refused{Method, Message} is now the error a bundle's JSON-RPC error answer produces. Error() returns the same text as before, so other callers are unchanged.
The runtime line now reads: <module> did not take <key> (event <x-event-id>): its handler answered an error, in its own words: "…" (an event is taken by any answer that is not an error); offered again in 5s. The runtime's own failures (no answer in time, bundle exited) are still reported as they were.
Rule, unchanged and now tested: any mesh/event answer that is not an error takes the event: {}, null, or no result at all. I considered whether an empty answer should count as taken and decided yes. Requiring a particular result would add a second way to fail that tells us nothing a handler means. The rule is documented in mesh-sdk's README ("Taking an event").
Tests:internal/launch/event_answer_test.go (a sh fake bundle: result, bare and error answers) and TestAnUntakenEventSaysWhoFailed in runtime. go test ./... passes. The NATS integration tests skip without nats-server.
On ace, `[mesh-tools] plex did not take radarr.download.completed: Unexpected end of JSON input; offered again` looked like the runtime failing to parse the bundle's answer. It wasn't. The runtime parses nothing in a `mesh/event` answer. That text was plex's own handler error (JSON.parse of an empty body), passed through by the SDK.
**Change (minimal):**
- `launch.Refused{Method, Message}` is now the error a bundle's JSON-RPC error answer produces. `Error()` returns the same text as before, so other callers are unchanged.
- The runtime line now reads: `<module> did not take <key> (event <x-event-id>): its handler answered an error, in its own words: "…" (an event is taken by any answer that is not an error); offered again in 5s`. The runtime's own failures (no answer in time, bundle exited) are still reported as they were.
**Rule, unchanged and now tested:** any `mesh/event` answer that is not an error takes the event: `{}`, `null`, or no result at all. I considered whether an empty answer should count as taken and decided yes. Requiring a particular result would add a second way to fail that tells us nothing a handler means. The rule is documented in mesh-sdk's README ("Taking an event").
**Tests:** `internal/launch/event_answer_test.go` (a sh fake bundle: result, bare and error answers) and `TestAnUntakenEventSaysWhoFailed` in runtime. `go test ./...` passes. The NATS integration tests skip without nats-server.
Related: novox/mesh-media-catalog#5, novox/mesh-sdk (0.1.13), novox/hq issue 276.
"plex did not take radarr.download.completed: Unexpected end of JSON input"
read as the runtime failing to parse the bundle's answer. It was plex's own
handler error, relayed. A bundle's error answer is now a launch.Refused, and
the line says the handler answered an error, quotes it, names the event id
and the delay before the next offer; the runtime's own failures (no answer
in time, bundle exited) are said as before.
The rule, unchanged and now tested: any mesh/event answer that is not an
error takes the event, whatever its result says, including none.
Deliverynovox/mesh-tools@c2a068311598 — delivered since 2026-10-06T23:05:28Z
Transitions
2026-10-06 23:05 (new) → proposed (announced): novox/mesh-tools#17 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-tools@c2a068311598` — **delivered** since 2026-10-06T23:05:28Z
**Transitions**
- 2026-10-06 23:05 (new) → proposed (announced): novox/mesh-tools#17 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.
On ace,
[mesh-tools] plex did not take radarr.download.completed: Unexpected end of JSON input; offered againlooked like the runtime failing to parse the bundle's answer. It wasn't. The runtime parses nothing in amesh/eventanswer. That text was plex's own handler error (JSON.parse of an empty body), passed through by the SDK.Change (minimal):
launch.Refused{Method, Message}is now the error a bundle's JSON-RPC error answer produces.Error()returns the same text as before, so other callers are unchanged.<module> did not take <key> (event <x-event-id>): its handler answered an error, in its own words: "…" (an event is taken by any answer that is not an error); offered again in 5s. The runtime's own failures (no answer in time, bundle exited) are still reported as they were.Rule, unchanged and now tested: any
mesh/eventanswer that is not an error takes the event:{},null, or no result at all. I considered whether an empty answer should count as taken and decided yes. Requiring a particular result would add a second way to fail that tells us nothing a handler means. The rule is documented in mesh-sdk's README ("Taking an event").Tests:
internal/launch/event_answer_test.go(a sh fake bundle: result, bare and error answers) andTestAnUntakenEventSaysWhoFailedin runtime.go test ./...passes. The NATS integration tests skip without nats-server.Related: novox/mesh-media-catalog#5, novox/mesh-sdk (0.1.13), novox/hq issue 276.
Delivery
novox/mesh-tools@c2a068311598— delivered since 2026-10-06T23:05:28ZTransitions
plans goThe commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.