HarperFast / HarperFast/harper

MQTT subscription publish drops the delete/put `type` discriminator — deletes arrive as a bare null

Open Beginner friendly
#2,438 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
89
Forks
10
Avg merge
2d 6h
Merged PRs (30d)
200

Description

## Summary

The MQTT publish path drops the `type` discriminator that the subscription listener already computed, so an MQTT subscriber cannot distinguish a **delete** from an **update whose value is null** — both arrive as a bare `null` payload. SSE and the REST WebSocket path forward the full envelope and are correct; the loss is MQTT-only.

## Mechanism (verified on `origin/main` @ `fb762a365`)

`server/DurableSubscriptionsSession.ts:352-358` forwards only `update.value` (plus `update.version` separately) to the MQTT wire listener:

```js
const result = await this.listener(
resourcePath + '/' + path,
update.value, // <-- update.type is never passed
messageId,
subscriptionRequest,
update.version
);
```

`resources/Table.ts`'s `subscribe()` listener has already computed `type` (`put` / `delete` / `invalidate`) by this point — it is discarded here rather than being unavailable.

Contrast `server/REST.ts:504-508` (the WebSocket path), which passes the whole `result.value` envelope — `type` included — into `serializeMessage()`.

## Blast radius

MQTT subscribers only. Every delete on every subscribed table. A subscriber maintaining a local materialized view has no way to tell "this record was deleted" from "this record's value is now null", so it either keeps deleted records or drops null-valued ones.

## Suggested fix

Forward the discriminator on the MQTT path the way the WS path does, rather than reconstructing it from a null value at the client. The fix site is the single call above.

*Found by the qa-explorer QA loop (finding F-181); dup-searched across `harper` and `harper-pro` in all states with six phrasings — nothing tracks this today.*

Contributor guide

Open the contributing guide

Research direction

Start in server/DurableSubscriptionsSession.ts:352-358, then inspect the subscription listener in resources/Table.ts and the contrasting WebSocket path in server/REST.ts:504-508. Verify the MQTT publish path preserves the computed put/delete/invalidate discriminator, and test that deletes remain distinguishable from updates whose value is null.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nodejs
Domain
backend, distributed-systems
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
90/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.