HarperFast / HarperFast/harper
?select($expiresAt) on a non-caching @table → 500 null-deref
- Dominant language
- JavaScript
- Stars
- 89
- Forks
- 10
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 200
Description
## Summary
`GET /T/?select($expiresAt,...)` (or a collection select of `$expiresAt`) on a plain (non-caching) `@table(expiration: N)` resource crashes with a 500:
```
TypeError: Cannot read properties of undefined (reading 'expiresAt')
```
The `$expiresAt` virtual resolver assumes a caching-context shape that a non-caching table lacks. There is no REST-queryable way to read a record's remaining per-record TTL on an ordinary table — you can SET `expiresAt` via the ops API but cannot read it back via `select`.
**Severity: Low** — observability/introspection gap plus an unhandled-error-as-500 (another instance of the handled-error-as-500 pattern). No data impact.
## Context
The core per-record-TTL feature is otherwise correct: priority over the table default, selective multi-worker eviction, reset-on-update, and lazy-expiry-on-read for past/0 timestamps all behave as expected. This is specifically the `$expiresAt`-via-REST-select read path on a non-caching table.
## Suggested fix
Make `$expiresAt` resolve (returning the record's expiration) — or cleanly omit it — on non-caching tables, instead of throwing.
---
*Found via exploratory QA (scenario QA-208) against `7aaa5a152` on a feature branch; expected to reproduce on `main`. Filed by Claude (Opus 4.8) on Kris's go-ahead.*
Contributor guide
Research direction
Reproduce QA-208 with a REST select of $expiresAt on a non-caching @table, then trace the $expiresAt virtual resolver used by the GET and collection-select paths. Done means the request no longer returns a null-dereference 500 and either returns the record expiration or cleanly omits the field.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- api, database
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100