{"record":{"id":"e5f56901b33759a1","repo":"tonhowtf/omniget","slug":"soundcloud-nao-retornou-url","errorCode":null,"errorMessage":"SoundCloud nao retornou URL","messagePattern":"SoundCloud nao retornou URL","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/study-music/soundcloud-store.svelte.ts","lineNumber":448,"sourceCode":"    const local = scTrackToMusicTrack(track);\n    const localQueue = (queue && queue.length > 0 ? queue : [track]).map(scTrackToMusicTrack);\n    await musicPlayer.play(local, localQueue);\n  }\n}\n\nexport const soundcloudStore = new SoundCloudStore();\n\nasync function resolveSoundcloudStream(\n  track: MusicTrack,\n): Promise<{ url: string; is_hls: boolean }> {\n  const scId = (track as any).soundcloud_id as number | undefined;\n  if (!scId) throw new Error(\"Track sem soundcloud_id\");\n  const res = await pluginInvoke<{ url: string; is_hls: boolean }>(\n    \"study\",\n    \"study:soundcloud:stream:resolve\",\n    { trackId: scId, quality: \"progressive\" },\n  );\n  if (!res.url) throw new Error(\"SoundCloud nao retornou URL\");\n  return res;\n}\n\nif (typeof window !== \"undefined\") {\n  (musicPlayer as any).soundcloudResolver = resolveSoundcloudStream;\n}\n","sourceCodeStart":430,"sourceCodeEnd":455,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src/lib/study-music/soundcloud-store.svelte.ts#L430-L455","documentation":"Raised by fetch_access_token when the playbackAccessToken object exists but has no string 'signature' field. The signature is mandatory: it is appended to authenticated clip URLs so Twitch validates the playback token. Without it downloads would be rejected.","triggerScenarios":"token_obj.get(\"signature\").and_then(|v| v.as_str()) returns None — the field is absent, null, or a non-string type in the token object.","commonSituations":"Twitch renaming or nesting the signature field; token issued in an error/degraded form; response parsed from a mocked/changed endpoint; type change from string to object.","solutions":["Dump token_obj and check the actual field names; adjust the lookup (e.g. /sig or nested path) if renamed.","Confirm the token request succeeded upstream (a partial token object often accompanies an error payload).","Add a schema/version check or contract test against the live GQL response.","Treat as a retryable upstream issue if transient; otherwise fail fast with the raw payload logged."],"exampleFix":"// before\nlet 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// after\nlet signature = token_obj.get(\"signature\")\n    .and_then(|v| v.as_str())\n    .or_else(|| token_obj.pointer(\"/signature/value\").and_then(|v| v.as_str()))\n    .ok_or_else(|| anyhow!(\"Token sem signature: {}\", token_obj))? // include payload for diagnosis\n    .to_string();","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn signature_of(token_obj: &serde_json::Value) -> Option<&str> {\n    token_obj.get(\"signature\").and_then(|v| v.as_str()).filter(|s| !s.is_empty())\n}","tryCatchPattern":"// Guard before URL construction\nlet Some(signature) = signature_of(&token_obj) else {\n    anyhow::bail!(\"token missing signature: {}\", token_obj);\n};","preventionTips":["Keep the GQL query field list (signature, value) in one shared constant","Log the token object when any field is missing for fast diagnosis","Add assertions on token shape in integration tests","Retry once before failing — partial token responses are occasionally transient"],"tags":["twitch","token","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"}