mastra-ai/mastra · error

Knowledge ceiling cannot be lowered from ${currentMaxScope}

Error message

Knowledge ceiling cannot be lowered from ${currentMaxScope} to ${maxScope}

What it means

Ceilings are monotonic: once a knowledge record's maxScope is set, assertKnowledgeCeilingRaised (used by raiseKnowledgeCeiling) only permits keeping or widening it. Since levels order org=0 < resource=1 < thread=2, a proposed maxScope with a higher order value means narrowing the ceiling, which is rejected to preserve previously-granted visibility guarantees.

Source

Thrown at packages/core/src/storage/domains/knowledge/base.ts:376

}

export function assertKnowledgeScopeWithinCeiling(scope: KnowledgeScope, maxScope?: KnowledgeScopeLevel): void {
  if (!maxScope) return;
  const reservedLevels = scope
    .map(entry => SCOPE_ORDER[entry.slice(0, entry.indexOf(':')) as KnowledgeScopeLevel])
    .filter((value): value is number => value !== undefined);
  const narrowestLevel = reservedLevels.length > 0 ? Math.max(...reservedLevels) : Number.MAX_SAFE_INTEGER;
  if (narrowestLevel < SCOPE_ORDER[maxScope]) {
    throw new Error(`Knowledge scope exceeds ${maxScope} ceiling`);
  }
}

export function assertKnowledgeCeilingRaised(
  currentMaxScope: KnowledgeScopeLevel | undefined,
  maxScope: KnowledgeScopeLevel | undefined,
): void {
  if (currentMaxScope && maxScope && SCOPE_ORDER[maxScope] > SCOPE_ORDER[currentMaxScope]) {
    throw new Error(`Knowledge ceiling cannot be lowered from ${currentMaxScope} to ${maxScope}`);
  }
}

export function parseKnowledgeWikilinks(text: string): string[] {
  const names: string[] = [];
  const seen = new Set<string>();
  let contentStart = -1;

  for (let index = 0; index < text.length - 1; index++) {
    const pair = text.slice(index, index + 2);
    if (pair === '[[') {
      contentStart = index + 2;
      index++;
      continue;
    }
    if (pair !== ']]' || contentStart < 0) continue;

    const name = text.slice(contentStart, index).trim();

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Only call raiseKnowledgeCeiling with the same level or a WIDER level (thread -> resource -> org direction).
  2. Fetch the current record first and skip the call if the desired maxScope equals the current one.
  3. If access must be reduced, rescope the record's scope or remove/restore records rather than lowering the ceiling.
  4. Fix stale-snapshot bugs: always read the latest record state before issuing ceiling updates.

Example fix

// before
await storage.raiseKnowledgeCeiling({ id, maxScope: 'org' }); // current is 'thread'
// after
const rec = await storage.getKnowledge({ id });
if (rec?.maxScope !== 'org') {
  // narrowing unsupported; rescope or delete instead
}
Defensive patterns

Strategy: try-catch

Validate before calling

const ORDER = { org: 0, resource: 1, thread: 2 } as const;
function isCeilingRaise(current?: keyof typeof ORDER, next?: keyof typeof ORDER): boolean {
  if (!current || !next) return true;
  return ORDER[next] <= ORDER[current]; // equal or wider is allowed
}

Try / catch

try {
  await storage.raiseKnowledgeCeiling({ id, maxScope });
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Knowledge ceiling cannot be lowered')) {
    const rec = await storage.getKnowledge({ id });
    // treat as no-op or handle reduction via rescope/delete; never lower the ceiling
  } else throw e;
}

Prevention

When it happens

Trigger: raiseKnowledgeCeiling({ id, maxScope: 'org' }) on a record whose current maxScope is 'resource' or 'thread'; passing a stale record snapshot's maxScope (older, narrower) back to the API; any attempt to tighten the ceiling after initially granting a wider one.

Common situations: Round-tripping a fetched record and accidentally writing back its old maxScope; admin tooling trying to revoke thread-level access (unsupported — must be handled by deletion/rescope of content instead); new ceiling set to the record's own creation level, which equals current, fine — but smaller than current throws.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/cf7b0e8ca39b9cb4. Report an issue: GitHub.