{"record":{"id":"7534d0f7cf866072","repo":"tonhowtf/omniget","slug":"token-sem-signature","errorCode":null,"errorMessage":"Token sem signature","messagePattern":"Token sem signature","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/twitch.rs","lineNumber":237,"sourceCode":"        if !response.status().is_success() {\n            return Err(anyhow!(\n                \"Twitch GQL token retornou HTTP {}\",\n                response.status()\n            ));\n        }\n\n        let json: serde_json::Value = response.json().await?;\n\n        let token_obj = json\n            .as_array()\n            .and_then(|arr| arr.first())\n            .and_then(|r| r.pointer(\"/data/clip/playbackAccessToken\"))\n            .ok_or_else(|| anyhow!(\"Access token not available for clip: {}\", slug))?;\n\n        let signature = token_obj\n            .get(\"signature\")\n            .and_then(|v| v.as_str())\n            .ok_or_else(|| anyhow!(\"Token sem signature\"))?\n            .to_string();\n\n        let value = token_obj\n            .get(\"value\")\n            .and_then(|v| v.as_str())\n            .ok_or_else(|| anyhow!(\"Token sem value\"))?\n            .to_string();\n\n        Ok(AccessToken { signature, value })\n    }\n\n    fn build_authenticated_url(source_url: &str, token: &AccessToken) -> String {\n        format!(\n            \"{}?sig={}&token={}\",\n            source_url,\n            urlencoding::encode(&token.signature),\n            urlencoding::encode(&token.value),\n        )","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/twitch.rs#L219-L255","documentation":"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\").","triggerScenarios":"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.","commonSituations":"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.","solutions":["Log the raw token_obj JSON to inspect what Twitch actually returned","Check for a GraphQL \"errors\" array in the response before reading signature","Verify the clip slug is valid and not removed/geo-blocked","Update the GQL query/parsing to match Twitch's current schema"],"exampleFix":"// before\nlet signature = token_obj.get(\"signature\").and_then(|v| v.as_str()).ok_or_else(|| anyhow!(\"Token sem signature\"))?.to_string();\n// after\nlet signature = token_obj.get(\"signature\")\n    .and_then(|v| v.as_str())\n    .ok_or_else(|| anyhow!(\"Token sem signature; raw response: {}\", token_obj))?;\nif token_obj.get(\"errors\").is_some() { anyhow::bail!(\"Twitch GQL error: {}\", token_obj[\"errors\"]); }","handlingStrategy":"try-catch","validationCode":"// Rust: pre-check parsed token object\nfn has_valid_token(obj: &serde_json::Value) -> bool {\n    obj.get(\"signature\").and_then(|v| v.as_str()).is_some()\n}","typeGuard":"fn is_string_field(v: &serde_json::Value, key: &str) -> Option<&str> {\n    v.get(key).and_then(|x| x.as_str())\n}","tryCatchPattern":"match fetch_access_token(slug).await {\n    Ok(token) => token,\n    Err(e) if e.to_string().contains(\"Token sem signature\") => {\n        // inspect raw response / retry / fallback\n        return Err(MediaError::Upstream(format!(\"twitch token unusable: {e}\")));\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Log the raw GQL response once during development to know the real schema","Check for a GraphQL \"errors\" array before parsing fields","Alert when this error rate spikes — it signals a Twitch schema change","Keep the GQL query hash/operation updated with the web client"],"tags":["twitch","missing-field","api-response","rust"],"backgroundTag":"unexpected-response-shape","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}