xai-org/grok-build · error
Server returned invalid verification URI
Error message
Server returned invalid verification URI
What it means
validate_verification_uri rejects verification URIs that cannot be safely shown/opened: any URI containing ASCII control characters, or a URI that fails url::Url::parse entirely. This protects users from opening malformed or attacker-crafted links supplied by the issuer.
Source
Thrown at crates/codegen/xai-grok-shell/src/auth/device_code.rs:516
use base64::Engine;
let parts: Vec<&str> = jwt.splitn(3, '.').collect();
if parts.len() < 2 {
return (String::new(), None);
}
let payload = match base64::engine::general_purpose::URL_SAFE_NO_PAD.decode(parts[1]) {
Ok(bytes) => bytes,
Err(_) => return (String::new(), None),
};
let claims: IdTokenClaims = match serde_json::from_slice(&payload) {
Ok(claims) => claims,
Err(_) => return (String::new(), None),
};
(claims.sub.unwrap_or_default(), claims.email)
}
fn validate_verification_uri(uri: &str) -> anyhow::Result<()> {
if uri.chars().any(|c| c.is_ascii_control()) {
anyhow::bail!("Server returned invalid verification URI");
}
let parsed = url::Url::parse(uri)
.map_err(|_| anyhow::anyhow!("Server returned invalid verification URI"))?;
match parsed.scheme() {
"https" => Ok(()),
"http" if matches!(parsed.host_str(), Some("localhost") | Some("127.0.0.1")) => Ok(()),
_ => anyhow::bail!("Server returned unsupported verification URI scheme"),
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::sync::Arc;
use super::{AuthManager, build_auth, validate_verification_uri};
use crate::auth::{AuthMode, GrokComConfig};View on GitHub (pinned to bc7f02eddd)
Solutions
- Inspect the device-authorization response to see the malformed verification_uri value.
- Fix the base URL / issuer configuration so the real xAI OAuth2 endpoint is used.
- If using a proxy, correct it to pass through the issuer's absolute https verification URI.
- Update the client/server pair so response shapes match.
Example fix
// before: server returns relative or dirty URI
{"verification_uri": "x.ai/device\n"}
// after: clean absolute URL
{"verification_uri": "https://x.ai/device"} Defensive patterns
Strategy: validation
Validate before calling
fn verification_uri_plausible(uri: &str) -> bool {
!uri.is_empty()
&& !uri.chars().any(char::is_control)
&& url::Url::parse(uri).is_ok()
}
// run on the response before invoking the login flow Type guard
fn checked_uri(uri: &str) -> Option<url::Url> {
if uri.chars().any(char::is_control) { return None; }
url::Url::parse(uri).ok()
} Try / catch
match request_device_code(&client, &cfg).await {
Err(e) if e.to_string().contains("invalid verification URI") => {
eprintln!("Issuer returned a bad verification URL; check base-URL config/proxy.");
}
other => other?,
} Prevention
- Point the client at the official issuer; avoid rewriting OAuth2 responses in proxies.
- Reject/mock servers that omit verification_uri rather than validating empty strings.
- Test the device endpoint's response shape after any issuer upgrade.
- Log raw responses when validation fails to spot HTML/error bodies.
When it happens
Trigger: request_device_code calls validate_verification_uri on server_resp.verification_uri (or verification_uri_complete) and the string contains control chars (e.g. \n, \t, \x1b) or is not a parseable absolute URL.
Common situations: A proxy or mock server returns an error body/HTML in the verification_uri field; issuer omits the field so an empty string is validated; environment variable misconfiguration points the client at a non-OAuth2 endpoint.
Related errors
- Server returned unsupported verification URI scheme
- Server returned invalid user_code format (expected [A-Z0-9-]
- Device code expired. Run `grok login --device-auth` again.
- Authorization denied. The user rejected the request.
- Token exchange error: {detail}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/1e6eb447bca52925.
Report an issue: GitHub.