tonhowtf/omniget · error
VOD não encontrado (ou já expirou)
Error message
VOD não encontrado (ou já expirou): {} What it means
video() looks up a VOD by id via GraphQL and throws when data.video is missing or null. Twitch returns null both for unknown ids and for VODs that expired or were deleted, so the message explicitly mentions expiry — a frequent cause given Twitch's 14/30/60-day retention limits.
Solutions
- Open https://www.twitch.tv/videos/<id> to confirm the VOD still exists
- Check whether the VOD expired per the channel's retention window — it cannot be recovered
- Correct the VOD id (must be the numeric id, not a title/slug)
- For sub-only VODs, ensure proper authentication cookies are supplied if supported
Example fix
// before
let v = gql.video("1234567890").await?; // expired VOD
// after
let v = match gql.video("1234567890").await {
Ok(v) => v,
Err(e) if e.to_string().contains("expirou") => {
eprintln!("VOD unavailable/expired; pick another");
return Err(e);
}
Err(e) => return Err(e),
}; Defensive patterns
Strategy: validation
Validate before calling
let id = input.trim();
if !id.chars().all(|c| c.is_ascii_digit()) {
eprintln!("VOD id must be numeric, got: {id}");
return Ok(());
}
// Optionally: check twitch.tv/videos/{id} is still live before calling. Try / catch
match gql.video(id).await {
Err(e) if e.to_string().contains("expirou") => {
eprintln!("VOD {id} is gone (expired/deleted); cannot recover");
Ok(())
}
other => other,
} Prevention
- Only use the numeric VOD id, not slugs or titles
- Check retention windows (14/30/60 days) before scheduling downloads
- Act quickly — expired or DMCA-removed VODs are unrecoverable
- For sub-only VODs, supply valid authentication if supported
When it happens
Trigger: Calling gql.video(id) (directly or via clip_video / resolve_chat_target) with an id whose VOD no longer exists: expired after the retention window, deleted by the broadcaster, removed by DMCA, or a mistyped id.
Common situations: Trying to download chat for an old VOD past its subscriber retention period; DMCA-deleted VODs; wrong id (using a slug or timestamp instead of the numeric VOD id); turbo/sub-only VODs accessed without auth.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- resposta sem comentários (o VOD tem replay de chat?)
- canal não encontrado
- clipe não encontrado
- esse clipe não tem VOD de origem, então não há chat
- resposta do Twitch GQL sem `data`
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/bf602f7884f968cc.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/omniget-core/src/core/tools/twitch/gql.rs:222
let data = self.query(&q).await?;
let u = data.get("user").filter(|v| !v.is_null());
let u = u.ok_or_else(|| anyhow!("canal não encontrado: {}", login))?;
Ok(Channel {
id: str_at(u, "id"),
login: str_at(u, "login"),
display_name: str_at(u, "displayName"),
avatar: str_at(u, "profileImageURL"),
})
}
pub async fn video(&self, id: &str) -> anyhow::Result<VideoInfo> {
let q = format!(
r#"{{ video(id: "{}") {{ id title lengthSeconds createdAt owner {{ login displayName }} }} }}"#,
escape(id)
);
let data = self.query(&q).await?;
let v = data.get("video").filter(|v| !v.is_null());
let v = v.ok_or_else(|| anyhow!("VOD não encontrado (ou já expirou): {}", id))?;
Ok(VideoInfo {
id: str_at(v, "id"),
title: str_at(v, "title"),
channel: v
.pointer("/owner/login")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_string(),
channel_display: v
.pointer("/owner/displayName")
.and_then(|x| x.as_str())
.unwrap_or_default()
.to_string(),
duration_seconds: v.get("lengthSeconds").and_then(num).unwrap_or_default(),
created_at: str_at(v, "createdAt"),
clip_offset: None,
clip_duration: None,
})View on GitHub (pinned to 8600b91f42)