hcengineering/platform · error · PlatformError
Low level storage should be specified
Error message
Low level storage should be specified
What it means
Triggers middleware's init() additionally requires context.lowLevelStorage (low-level direct storage access built by the LowLevel middleware). When it is null, init() throws unknownError('Low level storage should be specified').
Source
Thrown at foundations/server/packages/middleware/src/triggers.ts:92
}
async close (): Promise<void> {
clearInterval(this.intervalId)
}
static async create (ctx: MeasureContext, context: PipelineContext, next?: Middleware): Promise<Middleware> {
// we need to init triggers from model first.
const triggers = new TriggersMiddleware(context, next)
await triggers.init(ctx)
return triggers
}
async init (ctx: MeasureContext): Promise<void> {
if (this.context.storageAdapter == null) {
throw new PlatformError(unknownError('Storage adapter should be specified'))
}
if (this.context.lowLevelStorage == null) {
throw new PlatformError(unknownError('Low level storage should be specified'))
}
this.storageAdapter = this.context.storageAdapter
this.triggers.init(this.context.modelDb)
}
async tx (ctx: MeasureContext<SessionData>, tx: Tx[]): Promise<TxMiddlewareResult> {
await this.triggers.tx(tx)
const result = await this.provideTx(ctx, tx)
const ftx = filterBroadcastOnly(tx, this.context.hierarchy)
if (ftx.length > 0) {
await this.processDerived(ctx, ftx)
}
return result
}
private async processDerived (ctx: MeasureContext<SessionData>, txes: Tx[]): Promise<void> {
const findAll: SessionFindAll = async (ctx, _class, query, options) => {View on GitHub (pinned to 63e28dc964)
Solutions
- Add LowLevel middleware (with an AdapterManager configured) before Triggers in the pipeline
- Ensure middleware ordering so lowLevelStorage is populated before Triggers.init runs
- Verify AdapterManager is configured, since LowLevel.create refuses to build lowLevelStorage without it
Example fix
// before const pipeline = await Pipeline.create(ctx, context, [Triggers.create]) // no lowLevelStorage // after const pipeline = await Pipeline.create(ctx, context, [AdapterManager.create, LowLevel.create, Triggers.create])
Defensive patterns
Strategy: validation
Validate before calling
if (context.lowLevelStorage == null) {
throw new Error('LowLevel middleware must run before Triggers.init')
} Type guard
function hasLowLevelStorage(ctx: PipelineContext): ctx is PipelineContext & { lowLevelStorage: LowLevelStorage } {
return ctx.lowLevelStorage != null
} Try / catch
try {
await triggers.init(ctx)
} catch (err) {
if (isPlatformErrorWith(err, 'Low level storage should be specified')) {
throw new Error('add LowLevel middleware (with AdapterManager) before Triggers')
}
throw err
} Prevention
- Always include AdapterManager and LowLevel middleware before Triggers
- Enforce middleware ordering conventions in the pipeline factory
- Assert context.lowLevelStorage exists before init
- Cover full pipeline construction in integration tests
When it happens
Trigger: Calling init()/create() on Triggers middleware when the LowLevel middleware was not part of the pipeline (or ran after), leaving context.lowLevelStorage undefined.
Common situations: Pipeline missing the LowLevel middleware; middleware ordering places Triggers before LowLevel; custom context assembly that never populates lowLevelStorage.
Related errors
- Storage adapter should be specified
- Low level storage is not available
- No AdapterManager
- ReadonlyError
- Invalid storage config:${st}
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/9693b1ef54335420.
Report an issue: GitHub.