tonhowtf/omniget · error
Access token not available for clip
Error message
Access token not available for clip: {} What it means
Raised by fetch_access_token when the HTTP 200 GQL response does not contain [0]./data/clip/playbackAccessToken. The response either isn't the expected batched array, the persisted query was rejected, or the clip has no playback token (e.g. deleted or restricted).
Solutions
- Log the full JSON body to detect persisted-query 'PersistedQueryNotFound' errors
- Update TOKEN_HASH to the current hash
- Fall back to yt-dlp, which obtains playback URLs independently
- Retry once — token issuance can transiently fail
Example fix
// before
let token_obj = json.as_array().and_then(|arr| arr.first())
.and_then(|r| r.pointer("/data/clip/playbackAccessToken"))
.ok_or_else(|| anyhow!("Access token not available for clip: {}", slug))?;
// after
let err_msg = json.pointer("/0/errors/0/message").and_then(|v| v.as_str()).unwrap_or("missing playbackAccessToken");
let token_obj = json.as_array().and_then(|arr| arr.first())
.and_then(|r| r.pointer("/data/clip/playbackAccessToken"))
.ok_or_else(|| anyhow!("Access token unavailable for {}: {}", slug, err_msg))?; Defensive patterns
Strategy: fallback
Validate before calling
// Detect persisted-query rejection in a 200 response body:
let pq_rejected = body.contains("PersistedQueryNotFound"); Type guard
fn has_playback_token(json: &serde_json::Value) -> bool {
json.as_array().and_then(|a| a.first())
.and_then(|r| r.pointer("/data/clip/playbackAccessToken"))
.map(|t| t.get("value").and_then(|v| v.as_str()).is_some())
.unwrap_or(false)
} Try / catch
match downloader.get_media_info(url).await {
Err(e) if e.to_string().contains("Access token not available") => fallback_ytdlp_info(url).await,
other => other,
} Prevention
- Log the full 200 body — persisted-query errors arrive with HTTP 200
- Keep TOKEN_HASH updated after Twitch rotations
- Fall back to yt-dlp when token issuance fails; it does not need the GQL token
When it happens
Trigger: GQL returns 200 with a persisted-query error envelope (not an array, or data.clip null), or the clip exists in metadata lookup but playback is denied so playbackAccessToken is absent/null.
Common situations: Stale TOKEN_HASH after a Twitch rotation producing a 200 error payload; deleted/restricted clip; schema change renaming playbackAccessToken.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Clip not found
- Track sem soundcloud_id
- SoundCloud nao retornou URL
- Spotify SDK device not ready
- Track sem metadata pra resolver no YouTube
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/d0fc890cf55aeb6b.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/platforms/twitch.rs:232
.header("client-id", CLIENT_ID)
.json(&body)
.send()
.await?;
if !response.status().is_success() {
return Err(anyhow!(
"Twitch GQL token retornou HTTP {}",
response.status()
));
}
let json: serde_json::Value = response.json().await?;
let token_obj = json
.as_array()
.and_then(|arr| arr.first())
.and_then(|r| r.pointer("/data/clip/playbackAccessToken"))
.ok_or_else(|| anyhow!("Access token not available for clip: {}", slug))?;
let signature = token_obj
.get("signature")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Token sem signature"))?
.to_string();
let value = token_obj
.get("value")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Token sem value"))?
.to_string();
Ok(AccessToken { signature, value })
}
fn build_authenticated_url(source_url: &str, token: &AccessToken) -> String {
format!(View on GitHub (pinned to 8600b91f42)