{"record":{"id":"136a5a710f1cb925","repo":"decolua/9router","slug":"missing-zed-private-key-verifier-restart-the-logi","errorCode":null,"errorMessage":"Missing Zed private key verifier; restart the login flow","messagePattern":"Missing Zed private key verifier; restart the login flow","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/shared/zedAuth.js","lineNumber":67,"sourceCode":"\nfunction normalizeBaseUrl(baseUrl, fallback) {\n  return String(baseUrl || fallback).replace(/\\/+$/, \"\");\n}\n\nfunction zedUrl(config, key, path, fallbackBase) {\n  const base = normalizeBaseUrl(config?.[key], fallbackBase);\n  return `${base}${path}`;\n}\n\n/** Encode a PEM private key as an opaque verifier (flows through the OAuth codeVerifier slot). */\nexport function encodeZedPrivateKeyVerifier(privateKeyPem) {\n  return `${PRIVATE_KEY_PREFIX}${b64url(privateKeyPem)}`;\n}\n\nexport function decodeZedPrivateKeyVerifier(verifier) {\n  const value = String(verifier || \"\");\n  if (!value.startsWith(PRIVATE_KEY_PREFIX)) {\n    throw new Error(\"Missing Zed private key verifier; restart the login flow\");\n  }\n  return fromB64url(value.slice(PRIVATE_KEY_PREFIX.length));\n}\n\n/** Generate a fresh RSA keypair + the zed.dev native_app_signin URL for it. */\nexport function createZedNativeAuthData(config = {}, options = {}) {\n  const { publicKey, privateKey } = crypto.generateKeyPairSync(\"rsa\", {\n    modulusLength: 2048,\n    publicKeyEncoding: { type: \"pkcs1\", format: \"der\" },\n    privateKeyEncoding: { type: \"pkcs1\", format: \"pem\" },\n  });\n\n  const nativeAppPort = Number(\n    options.nativeAppPort || config.defaultNativeAppPort || 58443,\n  );\n  const systemId = options.systemId || crypto.randomUUID();\n  const publicKeyString = b64urlPadded(publicKey);\n  const signInUrl = new URL(","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/shared/zedAuth.js#L49-L85","documentation":"Zed native-app login stores an RSA private key wrapped in a special prefixed verifier string (PRIVATE_KEY_PREFIX + base64url). decodeZedPrivateKeyVerifier parses that verifier back into the PEM key material and throws when the input does not carry the expected prefix — meaning the value is not a valid private-key verifier from the current login flow.","triggerScenarios":"privateKey() is called with a verifier string that is empty, truncated, JSON-escaped, or was generated by an older build using a different prefix format — i.e. the string does not start with PRIVATE_KEY_PREFIX.","commonSituations":"User pasted only the callback URL instead of the stored verifier; app restarted mid-login so the in-memory verifier was lost; schema/format change between versions; verifier stored in DB was corrupted or trimmed.","solutions":["Restart the Zed login flow (createZedNativeAuthData) to generate a fresh keypair + verifier","Verify the stored verifier is the full prefixed string, not a truncated copy or the callback URL","Check for version mismatch — old verifiers from a previous prefix format are unrecoverable, re-auth","Ensure storage did not strip the prefix (e.g. URL parsing of the stored value)"],"exampleFix":"// before\nconst pem = decodeZedPrivateKeyVerifier(account.callbackUrl); // wrong field\n// after\nconst pem = decodeZedPrivateKeyVerifier(account.privateKeyVerifier);\nif (!account.privateKeyVerifier?.startsWith(PRIVATE_KEY_PREFIX)) await restartZedLogin(account);","handlingStrategy":"validation","validationCode":"function hasValidZedVerifier(v) {\n  return typeof v === \"string\" && v.startsWith(PRIVATE_KEY_PREFIX) && v.length > PRIVATE_KEY_PREFIX.length;\n}\nif (!hasValidZedVerifier(account.zedVerifier)) await startZedLogin(account);","typeGuard":"function isZedPrivateKeyVerifier(v) { return typeof v === \"string\" && v.startsWith(PRIVATE_KEY_PREFIX); }","tryCatchPattern":"try {\n  const pem = decodeZedPrivateKeyVerifier(verifier);\n} catch (e) {\n  if (/Missing Zed private key verifier/.test(e.message)) {\n    const fresh = await createZedNativeAuthData(config);\n    // restart login: present fresh.verificationUriComplete to the user\n  } else throw e;\n}","preventionTips":["Store the verifier verbatim — never trim or URL-decode it","Persist the verifier with the keypair, not in memory only","Treat this error as 'login state lost' → always restart the flow","Guard against version-format changes by checking the prefix before decode"],"tags":["zed","oauth","login-state","private-key"],"backgroundTag":"stale-login-flow-state","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}