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

  1. Add LowLevel middleware (with an AdapterManager configured) before Triggers in the pipeline
  2. Ensure middleware ordering so lowLevelStorage is populated before Triggers.init runs
  3. 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

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


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/9693b1ef54335420. Report an issue: GitHub.