{"record":{"id":"40b9dec96a948486","repo":"zeroclaw-labs/zeroclaw","slug":"pending-login-is-missing-code-verifier","errorCode":null,"errorMessage":"Pending {} login is missing code verifier","messagePattern":"Pending (.+?) login is missing code verifier","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/auth/mod.rs","lineNumber":985,"sourceCode":"    config: &Config,\n    model_provider: &str,\n) -> Result<Option<PendingOAuthLogin>> {\n    let path = pending_oauth_login_path(config, model_provider);\n    if !path.exists() {\n        return Ok(None);\n    }\n    let bytes = std::fs::read(&path)?;\n    if bytes.is_empty() {\n        return Ok(None);\n    }\n    let persisted: PendingOAuthLoginFile = serde_json::from_slice(&bytes)?;\n    let secret_store = pending_oauth_secret_store(config);\n    let code_verifier = if let Some(encrypted) = persisted.encrypted_code_verifier {\n        secret_store.decrypt(&encrypted)?\n    } else if let Some(plaintext) = persisted.code_verifier {\n        plaintext\n    } else {\n        anyhow::bail!(\"Pending {} login is missing code verifier\", model_provider);\n    };\n    Ok(Some(PendingOAuthLogin {\n        model_provider: persisted\n            .model_provider\n            .unwrap_or_else(|| model_provider.to_string()),\n        profile: persisted.profile,\n        code_verifier,\n        state: persisted.state,\n        created_at: persisted.created_at,\n    }))\n}\n\npub fn clear_pending_oauth_login(config: &Config, model_provider: &str) {\n    let path = pending_oauth_login_path(config, model_provider);\n    if let Ok(file) = std::fs::OpenOptions::new().write(true).open(&path) {\n        let _ = file.set_len(0);\n        let _ = file.sync_all();\n    }","sourceCodeStart":967,"sourceCodeEnd":1003,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/auth/mod.rs#L967-L1003","documentation":"load_pending_oauth_login reconstructs a PendingOAuthLogin from the persisted auth-<provider>-pending.json file. The PKCE code_verifier is mandatory to finish the OAuth exchange; it is read either from encrypted_code_verifier (decrypted via the secret store) or the legacy plaintext code_verifier field. If the file has neither field, resuming the login is impossible, so it bails with the provider name in the message.","triggerScenarios":"Resuming (`auth paste-redirect`) a pending login whose state file was written by an older zeroclaw version before verifier persistence existed, a hand-crafted/truncated pending file, or a file where the encrypted field failed to serialize. paste_redirect hits this when loading the saved login.","commonSituations":"Upgrading zeroclaw across versions while a login was left half-finished, editing or partially copying the state dir, or disk issues truncating the JSON.","solutions":["Discard the stale pending state by deleting auth-<provider>-pending.json under the zeroclaw state dir, then re-run `zeroclaw auth login --model-provider <provider>`","Keep the state dir and secret store (which decrypts encrypted_code_verifier) on the same machine/profile — moving only the JSON without the secret store makes the verifier unrecoverable","Complete the paste-redirect step in the same session/environment where `auth login` started"],"exampleFix":"# before: resuming a stale pending login\nzeroclaw auth paste-redirect --model-provider gemini\n\n# after: clear it and start fresh\nrm \"$(zeroclaw auth state-dir)/auth-gemini-pending.json\"\nzeroclaw auth login --model-provider gemini","handlingStrategy":"validation","validationCode":"let path = state_dir.join(format!(\"auth-{provider}-pending.json\"));\nif path.exists() {\n    let raw: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(&path)?)?;\n    let has_verifier = raw.get(\"encrypted_code_verifier\").is_some_and(|v| !v.is_null())\n        || raw.get(\"code_verifier\").is_some_and(|v| !v.is_null());\n    anyhow::ensure!(has_verifier, \"pending login for {provider} lacks a code verifier — delete {path} and re-login\");\n}","typeGuard":null,"tryCatchPattern":"match auth.load_pending_oauth_login(ctx, provider, profile).await {\n    Ok(Some(p)) => Ok(p),\n    Err(e) if e.to_string().contains(\"missing code verifier\") => {\n        let _ = std::fs::remove_file(pending_path); // clear stale state\n        anyhow::bail!(\"pending login unusable — re-run `zeroclaw auth login --model-provider {provider}`\");\n    }\n    other => other,\n}","preventionTips":["Finish login and paste-redirect in the same session and environment","When copying a state dir, copy the secret store too or pending verifiers become unreadable","After version upgrades, delete leftover auth-*-pending.json files before resuming"],"tags":["oauth2","pkce","pending-login","state-file","auth"],"backgroundTag":"oauth-pkce-verifier-missing","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}