HarperFast / HarperFast/harper
HdbError silently accepts a non-string message, blanking the log to a bare "Error" — retire the object-message construction
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 205
Description
## The anti-pattern to retire
`HdbError` silently accepts a **non-string** `message`. When a caller passes an object, the error's `.message` becomes that object, `.stack` gets overwritten with an empty `Error`'s stack (first line literally `"Error"`), and the log renderer — which by design emits only `.stack` — prints a bare `Error` with none of the actual diagnostic text. The presenting symptom is roles-validation failures logging as `Error` with no role/operation name, but the real fix is at the construction boundary, not that one call site: **`HdbError` should reject or stringify a non-string message so the misuse can't compile away a diagnostic anywhere.**
## Mechanism (on `origin/main` @ e16d9616)
1. `validation/role_validation.ts:307` — `generateRolePermResponse` calls `handleHDBError(new Error(), validationMessage, 400)` where `validationMessage` is an **object** (`{error: ROLE_PERMS_ERROR, ...validationErrors}`).
2. `utility/errors/hdbError.ts:35` — `this.message = errOrig.message ? errOrig.message : this.http_resp_msg`; `errOrig` is the empty `new Error()`, so `this.message` becomes the passed object.
3. `hdbError.ts:39-41` — `if (typeof this.message !== 'string') { this.stack = errOrig.stack; }` overwrites `.stack` with the empty Error's stack (`"Error"` + a frame).
4. `utility/logging/harper_logger.ts:1051` (`errorToLogString`, added for #1734) — `const base = typeof error?.stack === 'string' ? error.stack : errorToString(error)` logs `.stack` only (deliberately, to avoid dumping raw Error properties and leaking secrets — #1734). Net: the log line degrades to `Error`, no role/operation.
## The fix (retire, don't patch the symptom)
Make `HdbError`'s constructor **not accept a non-string message**: coerce it (`typeof message === 'string' ? message : errorToString(message)` / a stable serialization) at construction, or throw in development so the misuse surfaces. That closes the whole class — any current or future caller that hands `HdbError` an object gets a usable message and a real stack instead of a silently blanked log. The `role_validation.ts:307` call is then correct without special-casing.
**Follow-up sweep (name it in the fix):** `handleHDBError(new Error(), , ...)` at `role_validation.ts:307` is the confirmed exercised site, but other callers passing non-string messages were not swept — the constructor guard makes the sweep a safety net rather than a prerequisite.
## Existing work checked
#1734 (the `.stack`-only renderer that shapes the symptom) — CLOSED/merged 2026-07-10, does not address non-string message construction. #2203 (roles.yaml suffix-cascade) — OPEN, a *different* bug in the same neighborhood (its 2026-08-30 comment traces `role_validation.ts:148` → `role.ts:43`, never touches `HdbError`). Searched harper + harper-pro, issues+PRs, all states, on "HdbError message object" / "stack only error message" — no hits.
## What this does not prove
Whether other `handleHDBError` callers pass non-string messages (unswept — the constructor guard covers them regardless); whether any downstream consumer relies on `.message` being the object (none found).
---
*From dispatch QA finding F-304, verified against harper origin/main e16d9616, 2026-08-30.*
Contributor guide
Research direction
Start with utility/errors/hdbError.ts and utility/logging/harper_logger.ts:1051 to understand message and stack handling, then inspect validation/role_validation.ts:307 and sweep other handleHDBError callers. Run the relevant existing error and role-validation tests if present. Done means non-string messages retain usable diagnostic text and a real stack without breaking current logging behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, typescript
- Domain
- backend, observability
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100