linshenkx/prompt-optimizer · error · ContextError
CONTEXT_ERROR_CODES.INVALID_STORE
CONTEXT_ERROR_CODES.INVALID_STORE
Error message
Invalid document structure
What it means
parseJsonObject successfully ran JSON.parse but the result failed the isRecord check — the parsed value is not a plain object (it's an array, string, number, boolean, or null). This inner error is deliberately re-thrown verbatim in the catch block so it isn't confused with a JSON syntax failure.
Source
Thrown at packages/core/src/services/context/repo.ts:142
const doc: ContextStoreDoc = {
version: CONTEXT_STORE_VERSION,
currentId: DEFAULT_CONTEXT_CONFIG.id,
contexts: {
[DEFAULT_CONTEXT_CONFIG.id]: defaultContext
}
};
// 立即保存初始文档
await this.storage.setItem(CONTEXT_STORE_KEY, JSON.stringify(doc));
return doc;
}
try {
const doc = JSON.parse(data) as ContextStoreDoc;
// 基础验证
if (!doc.currentId || !doc.contexts || typeof doc.contexts !== 'object') {
throw new ContextError(CONTEXT_ERROR_CODES.INVALID_STORE, 'Invalid document structure');
}
// 迁移逻辑:为旧文档的上下文补写 mode 字段
let migrated = false;
for (const ctx of Object.values(doc.contexts)) {
if (!ctx.mode) {
ctx.mode = DEFAULT_CONTEXT_MODE;
migrated = true;
}
}
// 如果有迁移,需要保存回存储
if (migrated) {
await this.storage.setItem(CONTEXT_STORE_KEY, JSON.stringify(doc));
if (process.env.NODE_ENV === 'development') {
console.log('[ContextRepo] Migrated contexts to add mode field');
}
}View on GitHub (pinned to 3e677b1d9f)
Solutions
- Adjust the prompt to require a top-level JSON object with named keys
- If arrays are legitimately expected, accept them explicitly instead of relying on isRecord
- Fix the slice/extraction logic when it captures a non-object fragment
Example fix
// before modelInstruction = 'Return the results as JSON.' // after modelInstruction = 'Return the results as a single JSON OBJECT with keys "prompt" and "variables". Never return a top-level array.'
Defensive patterns
Strategy: type-guard
Validate before calling
function isJsonObjectText(text: string): boolean {
try {
return isRecord(JSON.parse(text))
} catch {
return false
}
} Type guard
function isRecord(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v)
} Try / catch
try {
const parsed = parseJsonObject(candidate)
} catch (error) {
if (error instanceof Error && error.message === 'Model response is not a valid JSON object') {
// valid JSON but wrong shape (array/scalar) — prompt fix needed, retrying may help
return coerceToObjectLikeShape(candidate)
}
throw error
} Prevention
- Prompt for a top-level object with named keys, never an array
- Check parsed shape with isRecord before using it
- If arrays are expected, wrap or accept them explicitly rather than relying on the record check
When it happens
Trigger: Calling the parse path with model output like '[1,2,3]', '"text"', '42', 'true', or 'null' — valid JSON whose top-level value is not a record.
Common situations: Model returns a JSON array instead of an object; model returns a bare string; extraction slice grabs a quoted string or number fragment from surrounding prose.
Related errors
- Optimized text must be a string
- CONTEXT_ERROR_CODES.IMPORT_FORMAT_ERROR
- Text comparison calculation failed: ${errorMessage}
- Message content must be a string
- Image at index ${index} is invalid
AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27).
Data as JSON: /api/errors/b24e8749ce3b5d93.
Report an issue: GitHub.