{"record":{"id":"507bb91e7b1b35fe","repo":"CherryHQ/cherry-studio","slug":"private-key-must-be-a-non-empty-string","errorCode":null,"errorMessage":"Private key must be a non-empty string","messagePattern":"Private key must be a non-empty string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/aiCore/src/core/providers/core/utils.ts","lineNumber":13,"sourceCode":"/**\n * Provider 工具函数和错误类\n * 合并自 utils.ts 和 errors.ts\n */\n\n// ==================== 私钥格式化工具 ====================\n\n/**\n * 格式化私钥，确保它包含正确的PEM头部和尾部\n */\nexport function formatPrivateKey(privateKey: string): string {\n  if (!privateKey || typeof privateKey !== 'string') {\n    throw new Error('Private key must be a non-empty string')\n  }\n\n  // 先处理 JSON 字符串中的转义换行符\n  const key = privateKey.replace(/\\\\n/g, '\\n')\n\n  // 检查是否已经是正确格式的 PEM 私钥\n  const hasBeginMarker = key.includes('-----BEGIN PRIVATE KEY-----')\n  const hasEndMarker = key.includes('-----END PRIVATE KEY-----')\n\n  if (hasBeginMarker && hasEndMarker) {\n    // 已经是 PEM 格式，但可能格式不规范，重新格式化\n    return normalizePemFormat(key)\n  }\n\n  // 如果没有完整的 PEM 头尾，尝试重新构建\n  return reconstructPemKey(key)\n}\n","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/aiCore/src/core/providers/core/utils.ts#L1-L31","documentation":"formatPrivateKey validates its input up front: a falsy or non-string value throws because all subsequent parsing (regex replace, PEM detection) assumes a real string. This is the entry guard for the private-key normalization helpers used when signing requests (e.g. JWT/Service Account auth).","triggerScenarios":"Calling formatPrivateKey(undefined), formatPrivateKey(''), formatPrivateKey(null), or passing a non-string (number, object) — usually because a service-account private key was not loaded from env/file.","commonSituations":"SERVICE_ACCOUNT_KEY / privateKey env var not set; reading the key from a JSON config that was parsed wrong; a form field left empty; passing the parsed JSON object instead of its privateKey field.","solutions":["Load the private key from its source (env var, file) and confirm it is a non-empty string before calling.","If the key lives in a service-account JSON, extract the privateKey field (json.private_key), not the whole object.","Fail fast at app startup with a clear message when the key is missing."],"exampleFix":"// before\nformatPrivateKey(process.env.SERVICE_ACCOUNT_KEY) // undefined if unset\n// after\nconst key = process.env.GCP_PRIVATE_KEY\nif (!key) throw new Error('GCP_PRIVATE_KEY not set')\nformatPrivateKey(key)","handlingStrategy":"validation","validationCode":"if (!privateKey || typeof privateKey !== 'string') {\n  throw new Error('privateKey is missing — set GCP_PRIVATE_KEY / service account key')\n}\nformatPrivateKey(privateKey)","typeGuard":"function isNonEmptyString(v: unknown): v is string {\n  return typeof v === 'string' && v.length > 0\n}","tryCatchPattern":null,"preventionTips":["Fail fast at startup if the key env var is unset.","Extract json.private_key from the service-account JSON, not the whole object.","Keep a single loader for the key so all call sites use validated input."],"tags":["auth","private-key","validation","config"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}