different-ai/openwork · error

The protected OpenWork credential key is invalid.

Error message

The protected OpenWork credential key is invalid.

What it means

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.

Source

Thrown at apps/desktop/electron/secure-vault-key.mjs:48

function backupTimestamp(date) {
  const parts = [
    date.getUTCFullYear(),
    date.getUTCMonth() + 1,
    date.getUTCDate(),
    date.getUTCHours(),
    date.getUTCMinutes(),
    date.getUTCSeconds(),
  ];
  return parts.map((part, index) => String(part).padStart(index === 0 ? 4 : 2, "0")).join("");
}

/**
 * @param {string} encoded
 */
function decodeKey(encoded) {
  const key = Buffer.from(encoded, "base64");
  if (key.byteLength !== KEY_BYTES) {
    throw new Error("The protected OpenWork credential key is invalid.");
  }
  return key;
}

/**
 * Creates a lazy key provider so Electron does not initialize secure storage
 * until a user opts into OpenWork-managed OAuth.
 *
 * @param {{
 *   filePath: string;
 *   loadSafeStorage: () => import("electron").SafeStorage;
 *   platform?: NodeJS.Platform;
 * }} options
 */
export function createDesktopVaultKeyProvider({
  filePath,
  loadSafeStorage,
  platform = process.platform,

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Delete the stored key file so the app regenerates a fresh one on next load
  2. Re-run the key provisioning/persist flow to rewrite the protected key
  3. Verify the encoded string is complete, valid base64, and produced by the same app version

Example fix

// before
decodeKey(fs.readFileSync(keyPath, 'utf8').trim()); // throws: wrong length
// after
const encoded = fs.readFileSync(keyPath, 'utf8').trim();
if (Buffer.from(encoded, 'base64').byteLength === KEY_BYTES) {
  decodeKey(encoded);
} else {
  fs.rmSync(keyPath); // regenerate
}
Defensive patterns

Strategy: try-catch

Validate before calling

function encodedKeyLooksValid(encoded) {
  try { return Buffer.from(encoded, 'base64').byteLength === KEY_BYTES; }
  catch { return false; }
}

Try / catch

try {
  const key = await getKey();
} catch (e) {
  if (e.message.includes('credential key is invalid')) {
    await regenerateKey(); // delete stored blob and re-provision
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/320c64266d234536. Report an issue: GitHub.