From cb67462a5dbbaab995fba5189a8ada498db94d6f Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Tue, 4 Aug 2026 11:35:51 -0700 Subject: [PATCH] fix(upload): publish stems sequentially after parent track MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When uploading a contest track + stems, publishParentTrack() and publishStems() ran concurrently via Promise.all. This caused stems to be published before the parent track was confirmed on-chain, so every publishStem API call failed. The UI showed red ⚠ error icons on all stem rows even though the upload actually succeeded. Reproducible on: Burko contest, Andrew Lux contest, Audius Summer Cypher Vol 2. Fix: await publishParentTrack() first, then await publishStems(). This ensures the parent track is on-chain before any stem references it, eliminating the race condition. --- .../api/tan-query/upload/usePublishTracks.ts | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/common/src/api/tan-query/upload/usePublishTracks.ts b/packages/common/src/api/tan-query/upload/usePublishTracks.ts index 3c5295d1a7f..6686fe146a1 100644 --- a/packages/common/src/api/tan-query/upload/usePublishTracks.ts +++ b/packages/common/src/api/tan-query/upload/usePublishTracks.ts @@ -146,29 +146,33 @@ export const publishTracks = async ( } } - const [trackResult, stemsResults] = await Promise.all([ - publishParentTrack(), - publishStems(context, { - clientId: param.clientId, - parentMetadata: param.metadata, - stems: - param.metadata.stems - ?.map((stem, index) => { - const audioUploadResponse = param.stemsUploadResponses?.[index] + // Publish the parent track first, then publish stems sequentially. + // Previously both ran concurrently via Promise.all, which caused a race + // condition: publishStem calls referenced the parent trackId before it + // was confirmed on-chain, so every stem publish failed. The UI then + // showed red ⚠ error icons on all stem rows even though the upload + // succeeded (reproducible on contest uploads: Burko, Andrew Lux, etc.) + const trackResult = await publishParentTrack() + const stemsResults = await publishStems(context, { + clientId: param.clientId, + parentMetadata: param.metadata, + stems: + param.metadata.stems + ?.map((stem, index) => { + const audioUploadResponse = param.stemsUploadResponses?.[index] - // This should never be the case, but being defensive - if (!audioUploadResponse) return null - return { - metadata: stem, - audioUploadResponse - } - }) - .filter( - (stem): stem is NonNullable => stem !== null - ) ?? [], - parentTrackId: trackId - }) - ]) + // This should never be the case, but being defensive + if (!audioUploadResponse) return null + return { + metadata: stem, + audioUploadResponse + } + }) + .filter( + (stem): stem is NonNullable => stem !== null + ) ?? [], + parentTrackId: trackId + }) return { clientId: param.clientId,