xai-org/grok-build · error

auth entry '{scope_key}' not found while persisting refreshe

Error message

auth entry '{scope_key}' not found while persisting refreshed token

What it means

write_refreshed_token in the hub auth module persists a refreshed OAuth token back into the shared auth.json file (JSON keyed by scope_key, e.g. per-scope entries). Before writing it takes a file lock, reads and parses auth.json, and looks up the top-level object entry named `scope_key`. This error is thrown when that entry is missing or is not a JSON object — i.e. the token was refreshed for a scope whose entry no longer exists on disk, so there is nowhere to persist the rotated access/refresh tokens.

Source

Thrown at crates/codegen/xai-grok-workspace/src/hub_auth/mod.rs:305

    // raced the shell's own refresh writer: whichever wrote second silently
    // rolled back the other's freshly rotated refresh token on disk — a
    // guaranteed future `invalid_grant` for every session sharing the file.
    let Some(_lock) = lock_auth_file(path) else {
        // The rotated token still serves this process from memory, so warn
        // rather than fail — but disk now trails the IdP by one rotation, and
        // a fresh process that picks it up will present a spent token.
        tracing::warn!(
            timeout = ?AUTH_LOCK_TIMEOUT,
            "auth.json.lock busy; skipping refreshed-token persist (disk left one rotation behind)"
        );
        return Ok(());
    };

    let content = std::fs::read_to_string(path)?;
    let mut raw: serde_json::Value = serde_json::from_str(&content)?;

    let Some(obj) = raw.get_mut(scope_key).and_then(|e| e.as_object_mut()) else {
        anyhow::bail!("auth entry '{scope_key}' not found while persisting refreshed token");
    };

    // Never roll disk back to an older token. Each refresh persists on its own
    // thread and a sibling shell writes the same file, so writes can arrive out
    // of order; the loser would replace a live refresh token with a spent one
    // and guarantee a future `invalid_grant`.
    if let Some(new_expiry) = event.expires_at
        && let Some(disk_expiry) = obj
            .get("expires_at")
            .and_then(serde_json::Value::as_str)
            .and_then(|s| chrono::DateTime::parse_from_rfc3339(s).ok())
        && disk_expiry.with_timezone(&chrono::Utc) >= new_expiry
    {
        tracing::debug!("auth.json already holds a same-or-newer token; skipping persist");
        return Ok(());
    }

    obj.insert(

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Check auth.json actually contains a top-level object entry named `scope_key` before a refresh cycle; re-authenticate (full login) to regenerate the entry if it is missing
  2. Recreate the missing entry via the library's initial auth/login flow rather than hand-editing, then retry the refresh persist
  3. Back up and regenerate auth.json if it is corrupt or has a non-object value under scope_key; ensure all sibling processes use the same schema/version
  4. Verify all concurrent shells/processes point at the same auth.json path and are the same tool version so no writer drops entries

Example fix

// before: auth.json lacks the scope entry after a sibling rewrite
write_refreshed_token(&auth_path, "workspace:rw", &event)?;
// after: ensure the entry exists before persisting
ensure_auth_entry(&auth_path, "workspace:rw")?; // re-login / seed from the refresh event if absent
write_refreshed_token(&auth_path, "workspace:rw", &event)?;
Defensive patterns

Strategy: validation

Validate before calling

fn auth_entry_exists(auth_path: &Path, scope_key: &str) -> bool {
    std::fs::read_to_string(auth_path).ok()
        .and_then(|c| serde_json::from_str::<serde_json::Value>(&c).ok())
        .map(|v| v.get(scope_key).map_or(false, |e| e.is_object()))
        .unwrap_or(false)
}
// call: if (!auth_entry_exists(&auth_path, "workspace:rw")) relogin();

Type guard

fn is_auth_entry(v: Option<&mut serde_json::Value>) -> Option<&mut serde_json::Map<String, serde_json::Value>> {
    v.and_then(|e| e.as_object_mut())
}

Try / catch

match write_refreshed_token(&auth_path, scope_key, &event) {
    Ok(()) => {}
    Err(e) if e.to_string().contains("not found while persisting refreshed token") => {
        // entry vanished (sibling rewrite / schema change): re-auth to regenerate it
        relogin_and_seed_auth_entry(&auth_path, scope_key, &event)?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling write_refreshed_token (via persist_on_refresh / write_refreshed_token* test helpers) with a scope_key that is absent from auth.json; the auth.json file was recreated/reset or truncated by a sibling process so the scope entry vanished; the entry under scope_key exists but is not a JSON object (corrupted schema); or a typo'd/scope-mismatched key is passed while the refresh event belongs to a different scope.

Common situations: Concurrent shells sharing one auth.json where one process recreates the file without the other's scope entry; auth.json edited manually or rewritten by a newer/older tool version with a different schema; running with an auth file generated for a different scope key after a scope rename; a stale in-memory session attempting to persist after the file was replaced.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/b42fdb94a00ebaa5. Report an issue: GitHub.