HarperFast / HarperFast/harper
null / empty-string / missing primary key is silently accepted and stored (REST/ops/SQL)
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
## Summary
A create whose primary key resolves to `null`, empty-string `""`, or missing is **silently accepted and committed** — across REST POST, ops `insert`, and SQL `INSERT` — with no validation rejection. The result is phantom rows that collide on the null key, can't be addressed by a normal PK GET, and pollute scans.
Confirmed on **main `541a3d33d` (v5.1.11)**.
## Reproduction
```
npm run test:integration -- "integrationTests/qa-scratch/qa228-empty-payloads.test.ts"
```
Observed (v5.1.11):
| Surface | Input | Result |
|---|---|---|
| REST POST | `{id: null}` | **201**, stored `id: null` |
| REST POST | `{id: ""}` | **201**, stored `id: ""` |
| ops `insert` | `[{id: null}]` | **200**, stored `id: null` |
| ops `insert` | `[{ }]` (no id) | **200**, stored (auto-key / null) |
| SQL `INSERT` | omit PK column | **200**, accepted |
The record is actually committed (this extends D-041, which had observed only the response-side null); a subsequent read/scan returns the phantom null/empty-PK rows.
## Defensible companions (NOT bugs)
For contrast, these are correct: `{}` on REST PUT uses the URL-path PK; `[]` via ops insert is a clean no-op (200, nothing stored); a `/Widget/null` path segment stores the **string** `"null"` (correct — it's a string key).
## Impact
Medium — silent-wrong data. A pipeline ingesting third-party JSON with a missing/null `id` silently accumulates junk rows instead of getting a clean 400. Multiple `id:null` creates collide.
## Suggested fix
Reject any create whose PK resolves to `null` / `undefined` / `""` with a clean **400** on all surfaces (REST / ops / SQL).
---
*Surfaced by the QA-explorer campaign (scenario QA-228), re-confirmed on `main` v5.1.11. Severity: medium. Related: the same sweep co-surfaced REST-write 500s on malformed/non-object bodies (filed separately). Filed for @kris.*
— Claude (Opus 4.8, 1M)
Contributor guide
Research direction
Run integrationTests/qa-scratch/qa228-empty-payloads.test.ts and compare the REST POST, ops insert, and SQL INSERT paths described in the issue. Trace how each surface resolves the primary key; done means null, undefined, empty-string, and missing keys are rejected with a clean 400 without storing rows, while the listed companion cases remain valid.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, nodejs, sql
- Domain
- api, backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100