fix(sdk): don't accept a transcode as done without its cid - #14585
fix(sdk): don't accept a transcode as done without its cid#14585dylanjeffers wants to merge 1 commit into
Conversation
pollProcessingStatus returned the moment a storage node reported status: done, without checking that the transcode result it is polling for is actually on the response. Upload rows replicate across nodes and getProcessingStatus talks to whichever node is selected, so a mirror can answer done from a row it has not finished catching up on. populateTrackMetadataWithUploadResponse then reads results['320'] off that response and gets undefined, and the track entity is written with no trackCid. The upload reports success. The track page loads, the artwork renders, people favorite and repost it - and there is no cid on the row pointing at the audio, so it can never be played and never records a single play. That is how DEZZE's only track ended up with 18 favorites, 16 reposts and 0 plays, and why share-to-story failed on it: the flow hands the 404ing stream URL to ffmpeg. Require the '320' result before treating an audio poll as finished. A node that really is done will have it on the next pass three seconds later, and an upload genuinely stuck in that state now times out with an error naming the cause instead of silently publishing unplayable audio. Image templates are unaffected - they have no '320' to wait for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Scope check: this fixes one of two failure modesI sampled the 250 most recent public tracks (a 9-hour window). 3 have no
The second mode is not explained by this change, and it raises a question I could not answer from the client alone: with the transcode still One concrete adjacent bug while I was in here, which I have deliberately not changed in this PR to keep it reviewable: if (template === 'audio' && resp.transcode_progress) {
// ... no-progress stall detection lives in here
}
|
The root cause behind the dead-track incident
Michael reported that one link failed when sharing to an Instagram story. The track behind it has no audio at all:
track_cidis null on the indexed row,/stream404s, and share-to-story hands that URL to ffmpeg, which fails. AudiusProject/api#1032 makes the API honest about it and repairs the rows; #14584 stops mobile offering a story for a track it cannot build a video from. This PR is why the rows exist in the first place.pollProcessingStatusreturned the moment a storage node reportedstatus: 'done':It never checked that the result it is polling for is on the response. Upload rows replicate across storage nodes and
getProcessingStatustalks to whichever nodestorageNodeSelectorhands back — falling over to others on error — so a mirror can legitimately answerdonefrom a row it has not finished catching up on, with an emptyresultsmap.populateTrackMetadataWithUploadResponseV2then does:which is
undefined, and the track entity is written without a cid. Nothing errors. The upload reports success, the track page loads, the artwork renders, people favorite and repost it — and there is no cid on the row pointing at the audio, so it can never be played and never records a play.That matches the failing track exactly:
duration,bpm,musical_key,orig_file_cidandaudio_upload_idwere all populated from the upload response, so the response was there and carriedprobeandaudio_analysis_results— onlyresults['320']was missing. The result: 0 plays against 18 favorites and 16 reposts.The change
Require the
'320'result before treating an audio poll as finished. A node that really is done will have it on the next pass three seconds later. An upload genuinely stuck in that state now times out with an error naming the cause —Upload reported done but no transcode result appeared within...— instead of silently publishing unplayable audio, which is a strictly better failure: the artist finds out at upload time rather than never.Image templates are untouched; they have no
'320'to wait for.Testing
Storage.test.ts: a node reporting done with no results keeps polling and picks up the cid on the retry; a response that already has the cid returns on the first call with no extra poll; image templates return immediately on an empty results map. All 5 tests in the file pass.Not reproduced against a live node — the race needs replication lag between storage nodes to trigger.
Related
is_streamablehonesty plus a repair job for rows already in this state🤖 Generated with Claude Code