{"record":{"id":"320c64266d234536","repo":"different-ai/openwork","slug":"the-protected-openwork-credential-key-is-invalid","errorCode":null,"errorMessage":"The protected OpenWork credential key is invalid.","messagePattern":"The protected OpenWork credential key is invalid\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/secure-vault-key.mjs","lineNumber":48,"sourceCode":"function backupTimestamp(date) {\n  const parts = [\n    date.getUTCFullYear(),\n    date.getUTCMonth() + 1,\n    date.getUTCDate(),\n    date.getUTCHours(),\n    date.getUTCMinutes(),\n    date.getUTCSeconds(),\n  ];\n  return parts.map((part, index) => String(part).padStart(index === 0 ? 4 : 2, \"0\")).join(\"\");\n}\n\n/**\n * @param {string} encoded\n */\nfunction decodeKey(encoded) {\n  const key = Buffer.from(encoded, \"base64\");\n  if (key.byteLength !== KEY_BYTES) {\n    throw new Error(\"The protected OpenWork credential key is invalid.\");\n  }\n  return key;\n}\n\n/**\n * Creates a lazy key provider so Electron does not initialize secure storage\n * until a user opts into OpenWork-managed OAuth.\n *\n * @param {{\n *   filePath: string;\n *   loadSafeStorage: () => import(\"electron\").SafeStorage;\n *   platform?: NodeJS.Platform;\n * }} options\n */\nexport function createDesktopVaultKeyProvider({\n  filePath,\n  loadSafeStorage,\n  platform = process.platform,","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/secure-vault-key.mjs#L30-L66","documentation":"Thrown by `decodeKey` in secure-vault-key.mjs when the stored/base64-encoded protected credential key does not decode to exactly `KEY_BYTES` bytes. The module stores an OS-encrypted key for OpenWork-managed OAuth in safeStorage; a decoded key of the wrong length means the stored blob is corrupt, truncated, or was produced by an incompatible version.","triggerScenarios":"Calling decodeKey (directly or via the lazy key provider) with a base64 string whose decoded byte length differs from KEY_BYTES — e.g. a truncated file, manual edits to the stored key file, or a key written by a different app version with a different key size.","commonSituations":"Hand-editing or partially copying the key file; disk corruption or interrupted write; migrating userData between machines with a stale key file; a version change that altered KEY_BYTES.","solutions":["Delete the stored key file so the app regenerates a fresh one on next load","Re-run the key provisioning/persist flow to rewrite the protected key","Verify the encoded string is complete, valid base64, and produced by the same app version"],"exampleFix":"// before\ndecodeKey(fs.readFileSync(keyPath, 'utf8').trim()); // throws: wrong length\n// after\nconst encoded = fs.readFileSync(keyPath, 'utf8').trim();\nif (Buffer.from(encoded, 'base64').byteLength === KEY_BYTES) {\n  decodeKey(encoded);\n} else {\n  fs.rmSync(keyPath); // regenerate\n}","handlingStrategy":"try-catch","validationCode":"function encodedKeyLooksValid(encoded) {\n  try { return Buffer.from(encoded, 'base64').byteLength === KEY_BYTES; }\n  catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const key = await getKey();\n} catch (e) {\n  if (e.message.includes('credential key is invalid')) {\n    await regenerateKey(); // delete stored blob and re-provision\n  } else throw e;\n}","preventionTips":["Never hand-edit the stored key file","Delete stale key files when migrating userData between machines","Add a length check before decode in custom tooling","Regenerate the key rather than trying to repair it"],"tags":["crypto","corrupt-data","oauth","electron"],"backgroundTag":"invalid-encryption-key","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}