tonhowtf/omniget · error

Token sem signature

Error message

Token sem signature

What it means

Thrown in fetch_access_token when Twitch's clip playback access token JSON response contains no string field named "signature". The library requires both signature and value to construct an authenticated playback URL; without the signature the token is unusable, so it fails fast with this Portuguese message ("Token without signature").

Solutions

  1. Log the raw token_obj JSON to inspect what Twitch actually returned
  2. Check for a GraphQL "errors" array in the response before reading signature
  3. Verify the clip slug is valid and not removed/geo-blocked
  4. Update the GQL query/parsing to match Twitch's current schema

Example fix

// before
let signature = token_obj.get("signature").and_then(|v| v.as_str()).ok_or_else(|| anyhow!("Token sem signature"))?.to_string();
// after
let signature = token_obj.get("signature")
    .and_then(|v| v.as_str())
    .ok_or_else(|| anyhow!("Token sem signature; raw response: {}", token_obj))?;
if token_obj.get("errors").is_some() { anyhow::bail!("Twitch GQL error: {}", token_obj["errors"]); }
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: pre-check parsed token object
fn has_valid_token(obj: &serde_json::Value) -> bool {
    obj.get("signature").and_then(|v| v.as_str()).is_some()
}

Type guard

fn is_string_field(v: &serde_json::Value, key: &str) -> Option<&str> {
    v.get(key).and_then(|x| x.as_str())
}

Try / catch

match fetch_access_token(slug).await {
    Ok(token) => token,
    Err(e) if e.to_string().contains("Token sem signature") => {
        // inspect raw response / retry / fallback
        return Err(MediaError::Upstream(format!("twitch token unusable: {e}")));
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Twitch's GQL PlaybackAccessToken query succeeds but the playbackAccessToken object lacks a string "signature" field — e.g. the API returned an error object, null fields, or changed its response schema.

Common situations: Twitch changes its GraphQL response shape; clip is geo-blocked or removed and Twitch returns partial JSON; response is actually an errors array instead of data.clip.playbackAccessToken content.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/7534d0f7cf866072. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/twitch.rs:237

        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!(
            "{}?sig={}&token={}",
            source_url,
            urlencoding::encode(&token.signature),
            urlencoding::encode(&token.value),
        )

View on GitHub (pinned to 8600b91f42)