tonhowtf/omniget · error · Error

Track sem soundcloud_id

Error message

Track sem soundcloud_id

What it means

Raised by fetch_access_token when the token GQL response is valid JSON but does not contain /data/clip/playbackAccessToken on the first array element. The response is expected to be an array of query results; without the playbackAccessToken object no download URLs can be signed.

Solutions

  1. Log the full token response JSON and compare against the expected {data:{clip:{playbackAccessToken:{signature,value}}}} shape; update pointers if it changed.
  2. Check whether the clip requires authentication (sub-only/age-gated) and add OAuth if so.
  3. Handle the empty-array case explicitly to distinguish 'no response' from 'wrong shape'.
  4. Test the same GQL query manually (curl with client-id) to confirm it still works.

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 token_obj = json.as_array()
    .and_then(|arr| arr.first())
    .and_then(|r| r.pointer("/data/clip/playbackAccessToken"));
let token_obj = match token_obj {
    Some(t) if !t.is_null() => t,
    _ => anyhow::bail!("No playbackAccessToken for {} — response: {}", slug, json),
};
Defensive patterns

Strategy: type-guard

Type guard

fn extract_token(json: &serde_json::Value) -> Option<&serde_json::Value> {
    json.as_array()?
        .first()?
        .pointer("/data/clip/playbackAccessToken")
        .filter(|t| t.get("signature").is_some() && t.get("value").is_some())
}

Try / catch

// Narrow the response before using it
match extract_token(&json) {
    Some(token) => build_url(token),
    None => bail!("unexpected token response: {}", json),
}

Prevention

When it happens

Trigger: json.as_array() is not an array, first() is missing (empty array), or the first element lacks data.clip.playbackAccessToken — combined via the and_then chain into ok_or_else.

Common situations: Twitch changing the GQL response envelope (no longer an array); token request rejected silently for restricted/geo-blocked clips; schema change renaming playbackAccessToken; anonymous token requests denied for some clips.

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


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

Appendix: source

Thrown at src/lib/study-music/soundcloud-store.svelte.ts:442

    }
    await pluginInvoke("study", "study:soundcloud:like:add", { trackId });
    return true;
  }

  async playTrack(track: ScTrack, queue?: ScTrack[]): Promise<void> {
    const local = scTrackToMusicTrack(track);
    const localQueue = (queue && queue.length > 0 ? queue : [track]).map(scTrackToMusicTrack);
    await musicPlayer.play(local, localQueue);
  }
}

export const soundcloudStore = new SoundCloudStore();

async function resolveSoundcloudStream(
  track: MusicTrack,
): Promise<{ url: string; is_hls: boolean }> {
  const scId = (track as any).soundcloud_id as number | undefined;
  if (!scId) throw new Error("Track sem soundcloud_id");
  const res = await pluginInvoke<{ url: string; is_hls: boolean }>(
    "study",
    "study:soundcloud:stream:resolve",
    { trackId: scId, quality: "progressive" },
  );
  if (!res.url) throw new Error("SoundCloud nao retornou URL");
  return res;
}

if (typeof window !== "undefined") {
  (musicPlayer as any).soundcloudResolver = resolveSoundcloudStream;
}

View on GitHub (pinned to 8600b91f42)