{"record":{"id":"76c1c7fd2c09c0a8","repo":"CherryHQ/cherry-studio","slug":"private-key-contains-invalid-characters-not-valid","errorCode":null,"errorMessage":"Private key contains invalid characters (not valid Base64)","messagePattern":"Private key contains invalid characters \\(not valid Base64\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/aiCore/src/core/providers/core/utils.ts","lineNumber":86,"sourceCode":"}\n\n/**\n * 重新构建 PEM 私钥\n */\nfunction reconstructPemKey(key: string): string {\n  // 移除所有空白字符和可能存在的不完整头尾\n  let cleanKey = key.replace(/\\s+/g, '')\n  cleanKey = cleanKey.replace(/-----BEGIN[^-]*-----/g, '')\n  cleanKey = cleanKey.replace(/-----END[^-]*-----/g, '')\n\n  // 确保私钥内容不为空\n  if (!cleanKey) {\n    throw new Error('Private key content is empty after cleaning')\n  }\n\n  // 验证是否是有效的 Base64 字符\n  if (!/^[A-Za-z0-9+/=]+$/.test(cleanKey)) {\n    throw new Error('Private key contains invalid characters (not valid Base64)')\n  }\n\n  // 格式化为 64 字符一行\n  const formattedKey = cleanKey.match(/.{1,64}/g)?.join('\\n') || cleanKey\n\n  return `-----BEGIN PRIVATE KEY-----\\n${formattedKey}\\n-----END PRIVATE KEY-----`\n}\n\n// ==================== 错误类 ====================\n\n/**\n * Provider 创建错误\n * 当创建 provider 实例失败时抛出\n */\nexport class ProviderCreationError extends Error {\n  constructor(\n    message: string,\n    public providerId: string,","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/aiCore/src/core/providers/core/utils.ts#L68-L104","documentation":"After reconstructPemKey cleans the input to raw base64, it validates the remainder matches ^[A-Za-z0-9+/=]+$. Any other character means the content is not valid base64 and would produce an invalid key, so it throws. This catches keys that contain JSON, prose, or wrong encoding.","triggerScenarios":"The cleaned key string contains characters outside base64 alphabet: JSON braces/braces/colons, PEM armor fragments not caught by the marker regex, URL-encoded characters, or binary/garbage data.","commonSituations":"Passing the entire service-account JSON object stringified (with {, \", :) instead of just private_key; a key with embedded '-----BEGIN RSA PRIVATE KEY-----' (the BEGIN regex needs no '-' after BEGIN so 'RSA ' breaks it); URL-safe base64 using - and _; copy-paste with smart quotes.","solutions":["If you have a service-account JSON, extract and pass only json.private_key.","Ensure the key is standard base64 (A–Z, a–z, 0–9, +, /, =).","Remove any non-base64 characters or re-export the key cleanly."],"exampleFix":"// before — passing the whole JSON\nformatPrivateKey(JSON.stringify(serviceAccountJson))\n// after — pass only the private_key field\nformatPrivateKey(serviceAccountJson.private_key)","handlingStrategy":"validation","validationCode":"const cleaned = privateKey.replace(/-----[^-]+-----/g, '').replace(/\\s+/g, '')\nif (!/^[A-Za-z0-9+/=]+$/.test(cleaned)) throw new Error('private key contains non-base64 chars (did you pass the whole JSON?)')\nformatPrivateKey(privateKey)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pass only json.private_key, never the whole service-account JSON.","Ensure the key is standard base64 (no - or _ characters).","Strip smart quotes and stray characters before formatting."],"tags":["auth","private-key","pem","base64","validation"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}