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
- Only call raiseKnowledgeCeiling with the same level or a WIDER level (thread -> resource -> org direction).
- Fetch the current record first and skip the call if the desired maxScope equals the current one.
- If access must be reduced, rescope the record's scope or remove/restore records rather than lowering the ceiling.
- 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
- Fetch the record and compare maxScope before calling raiseKnowledgeCeiling.
- Skip the call when the desired ceiling equals the current one.
- Never write a stale maxScope back from an old record snapshot.
- Reduce access via rescopeKnowledge or record removal, not ceiling lowering.
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
- Invalid knowledge node cursor.
- Knowledge node cursor does not match the active browse filte
- Invalid knowledge scope entry: ${entry}
- Knowledge scope contains multiple ${level} entries
- Knowledge scope cannot be empty
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/cf7b0e8ca39b9cb4.
Report an issue: GitHub.