{"record":{"id":"0e417308213c9be4","repo":"linshenkx/prompt-optimizer","slug":"favorite-prompt-content-cannot-be-empty","errorCode":null,"errorMessage":"Favorite prompt content cannot be empty","messagePattern":"Favorite prompt content cannot be empty","errorType":"validation","errorClass":"FavoriteValidationError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/favorite/storage-guards.ts","lineNumber":169,"sourceCode":"\n  return {\n    totalBytes,\n    softLimitExceeded,\n  }\n}\n\nexport const normalizeFavoriteRecord = (\n  value: unknown,\n  fallbackTimestamp = Date.now(),\n): FavoritePrompt => {\n  if (!isPlainObject(value)) {\n    throw new FavoriteValidationError('Favorite entry must be an object')\n  }\n\n  const raw = value as Record<string, unknown>\n  const rawContent = typeof raw.content === 'string' ? raw.content : ''\n  if (!rawContent.trim()) {\n    throw new FavoriteValidationError('Favorite prompt content cannot be empty')\n  }\n\n  const content = rawContent\n  const metadata = isPlainObject(raw.metadata) ? { ...raw.metadata } : undefined\n  const originalContent = toTrimmedString(raw.originalContent)\n  const nextMetadata = originalContent\n    ? { ...(metadata || {}), originalContent }\n    : metadata\n\n  assertFavoriteMetadataHasNoInlineImages(nextMetadata)\n\n  let functionMode: FavoritePrompt['functionMode'] = isFunctionMode(raw.functionMode)\n    ? raw.functionMode\n    : 'basic'\n  let optimizationMode: FavoritePrompt['optimizationMode'] = isOptimizationMode(\n    raw.optimizationMode,\n  )\n    ? raw.optimizationMode","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/favorite/storage-guards.ts#L151-L187","documentation":"normalizeFavoriteRecord requires a non-empty string content: raw.content must be a string that is non-empty after trimming. Missing content, non-string content, or whitespace-only content throws FavoriteValidationError ('Favorite prompt content cannot be empty').","triggerScenarios":"A record read from storage (or passed by the caller) whose content field is absent, null, a non-string, or only whitespace.","commonSituations":"Partial writes that saved objects without content, records from older versions using a different field name (text/prompt), or truncated/corrupted storage.","solutions":["Backfill content from a legacy field (text/prompt) before normalizing","Remove corrupted empty records from persisted data","Ensure all writers validate content is a non-empty string before saving"],"exampleFix":"// before\nconst fav = normalizeFavoriteRecord(record); // record.content = ''\n\n// after\nconst fixed = { ...record, content: record.content?.trim() || record.text || 'untitled' };\nconst fav = normalizeFavoriteRecord(fixed);","handlingStrategy":"validation","validationCode":"const hasNonEmptyContent = (r: Record<string, unknown>) =>\n  typeof r.content === 'string' && r.content.trim().length > 0;\nconst repaired = records.map(r =>\n  hasNonEmptyContent(r) ? r : { ...r, content: (r as any).text ?? (r as any).prompt ?? 'untitled' }\n);","typeGuard":"const hasNonEmptyContent = (r: unknown): r is { content: string } =>\n  typeof (r as any)?.content === 'string' && ((r as any).content as string).trim().length > 0;","tryCatchPattern":"try {\n  const fav = normalizeFavoriteRecord(value);\n} catch (e) {\n  if (e instanceof FavoriteValidationError && /content cannot be empty/.test(e.message)) {\n    // backfill from a legacy field or drop the corrupted record\n  }\n}","preventionTips":["Require non-empty trimmed content in the UI before allowing save","Backfill legacy field names (text/prompt) to content during migration","Drop or quarantine corrupted empty records instead of retrying them"],"tags":["validation","content","favorites","storage"],"backgroundTag":"schema-validation-failed","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}