Summary
On a database migrated LMDB→RocksDB via storage.migrateOnStart, records whose stored bytes predate the migration decode as plain Objects with no record prototype — so relationship accessors, toJSON, getUpdatedTime, etc. are unreachable on exactly those records. Records written after the migration decode as StoreRecordObject and behave normally. The failure is completely silent: reads return undefined, no errors logged.
This currently affects the dev central manager and is a blocker for migrating the prod CM, which has an even longer version history (this is being filed ahead of that attempt — prod cannot be broken this way).
Environment
- Dev CM:
harperfast/harper-pro:5.2.0-beta.3, RocksDB (migrated from LMDB via storage.migrateOnStart)
- History (per Devin): started on an early 4.7 beta, moved through many 4.7.x releases over months → harper-pro 5.1.x (still LMDB — no Rocks migration at this step) → 5.2.0-beta.x, where
storage.migrateOnStart performed the LMDB→RocksDB migration. The affected Organization record (2026-04-29) was written by an early-4.7-era build.
- CM component:
loadAsInstance = false resources, graphql schema with @relationships
Evidence (live inspector evals on the dev CM process)
The table is healthy — struct prototype has all relationship getters, resolvers installed:
{"protoNames":["constructor","allowRead","roles","clusters","settings"],
"attrRels":[{"name":"roles","hasResolve":true},{"name":"clusters","hasResolve":true},{"name":"settings","hasResolve":true}]}
But per-record prototype linkage depends on when the record was last written:
| record |
last written |
decodes as |
relationship getters |
| Cluster created post-migration |
5.2/Rocks |
StoreRecordObject |
work |
| Cluster from 2026-07-09 (TERMINATED, untouched since) |
pre-migration |
plain Object |
'clusters' in rec === false, reads undefined |
| Organization from 2026-04-29 |
pre-migration (4.7-era) |
plain Object |
unreachable |
| Host records (re-patched constantly by heartbeats) |
post-migration |
StoreRecordObject |
work |
Downstream, this produced: GET /Organization/:id silently missing its clusters/settings relationship properties, and roleData.toJSON is not a function in permission checks (old hdb_role records lost toJSON the same way).
What does NOT reproduce it (important)
A clean-room reproduction of the same path does not show the bug: seed data on harperdb/harperdb:4.7.33 → boot harper-pro:5.2.0-beta.3 with storage.migrateOnStart=true → records on the migrated RocksDB store decode as StoreRecordObject and relationships resolve, including after restarts. So the trigger involves something in the real instance's history. Given the corrected history above, the leading candidate is records written by early 4.7 betas / older 4.7.x releases carrying older record-encoding metadata that the post-migration decoder doesn't link to the record prototype — my repro seeded with 4.7.33 (late 4.7), which likely already wrote the newer encoding. Other candidates: the migration ran under an earlier 5.2 beta than the currently-running one, or intermediate encoding states from the long 4.7.x upgrade chain.
Instance preserved for investigation
We are deliberately leaving the dev CM in the broken state (not running the rewrite workaround) so core can inspect affected records in situ — ping @Devin-Holland or devain for access/evals.
Observed workaround
Rewriting a record heals it (the constantly-patched Host records all decode correctly). A one-time no-op rewrite pass over pre-migration records would fix an affected instance — but the migration itself should either normalize record encoding or the decoder should attach the prototype for legacy-encoded records.
Asks
- Identify which per-record encoding state causes prototype-less decode after migration (dev CM is available for live inspection — happy to capture raw entry/encoder metadata for an affected vs healthy record pair on request).
- Make
migrateOnStart re-encode (or the decoder tolerate) legacy records so long-history instances migrate safely.
- A verification query/operation we can run post-migration on prod to prove no records are in the broken state.
— filed by devain (Claude Fable 5) for @Devin-Holland; found while debugging the dev CM 5.2 upgrade (see central-manager#530 for the application-side fallout)
Summary
On a database migrated LMDB→RocksDB via
storage.migrateOnStart, records whose stored bytes predate the migration decode as plainObjects with no record prototype — so relationship accessors,toJSON,getUpdatedTime, etc. are unreachable on exactly those records. Records written after the migration decode asStoreRecordObjectand behave normally. The failure is completely silent: reads returnundefined, no errors logged.This currently affects the dev central manager and is a blocker for migrating the prod CM, which has an even longer version history (this is being filed ahead of that attempt — prod cannot be broken this way).
Environment
harperfast/harper-pro:5.2.0-beta.3, RocksDB (migrated from LMDB viastorage.migrateOnStart)storage.migrateOnStartperformed the LMDB→RocksDB migration. The affected Organization record (2026-04-29) was written by an early-4.7-era build.loadAsInstance = falseresources, graphql schema with@relationshipsEvidence (live inspector evals on the dev CM process)
The table is healthy — struct prototype has all relationship getters, resolvers installed:
{"protoNames":["constructor","allowRead","roles","clusters","settings"], "attrRels":[{"name":"roles","hasResolve":true},{"name":"clusters","hasResolve":true},{"name":"settings","hasResolve":true}]}But per-record prototype linkage depends on when the record was last written:
StoreRecordObjectObject'clusters' in rec === false, readsundefinedObjectStoreRecordObjectDownstream, this produced:
GET /Organization/:idsilently missing itsclusters/settingsrelationship properties, androleData.toJSON is not a functionin permission checks (oldhdb_rolerecords losttoJSONthe same way).What does NOT reproduce it (important)
A clean-room reproduction of the same path does not show the bug: seed data on
harperdb/harperdb:4.7.33→ bootharper-pro:5.2.0-beta.3withstorage.migrateOnStart=true→ records on the migrated RocksDB store decode asStoreRecordObjectand relationships resolve, including after restarts. So the trigger involves something in the real instance's history. Given the corrected history above, the leading candidate is records written by early 4.7 betas / older 4.7.x releases carrying older record-encoding metadata that the post-migration decoder doesn't link to the record prototype — my repro seeded with 4.7.33 (late 4.7), which likely already wrote the newer encoding. Other candidates: the migration ran under an earlier 5.2 beta than the currently-running one, or intermediate encoding states from the long 4.7.x upgrade chain.Instance preserved for investigation
We are deliberately leaving the dev CM in the broken state (not running the rewrite workaround) so core can inspect affected records in situ — ping @Devin-Holland or devain for access/evals.
Observed workaround
Rewriting a record heals it (the constantly-patched Host records all decode correctly). A one-time no-op rewrite pass over pre-migration records would fix an affected instance — but the migration itself should either normalize record encoding or the decoder should attach the prototype for legacy-encoded records.
Asks
migrateOnStartre-encode (or the decoder tolerate) legacy records so long-history instances migrate safely.— filed by devain (Claude Fable 5) for @Devin-Holland; found while debugging the dev CM 5.2 upgrade (see central-manager#530 for the application-side fallout)