When uploading videos to the YouTube service using the API, the API response indicated that the upload is successful. But on the website channel view the video is stuck at a 0% upload status (see screenshot). The percentage does not increase and the video never becomes viewable.
I am using the typescript/javascript api client library and the upload code looks as follows:
import fs from 'fs';import { google, Auth } from 'googleapis';async function uploadYouTubeVideoWithAuth( auth: Auth.OAuth2Client, videoLocalLocation: string, title: string, description: string): Promise<string> { google.options({ auth }); const youtube = google.youtube('v3'); const resp = await youtube.videos.insert({ part: ['id', 'snippet', 'status'], requestBody: { snippet: { title, description, }, status: { privacyStatus: 'private', selfDeclaredMadeForKids: false, }, }, media: { body: fs.createReadStream(videoLocalLocation), }, }); console.log(JSON.stringify(resp, null, 2)); const videoId = resp.data.id; if (!videoId) { throw new Error('failed to get videoId of uploaded video'); } return videoId;}The JSON response from the request looks as follows:
{"config": {"url": "https://youtube.googleapis.com/upload/youtube/v3/videos?part=id&part=snippet&part=status&uploadType=multipart","method": "POST","apiVersion": "","userAgentDirectives": [ {"product": "google-api-nodejs-client","version": "7.2.0","comment": "gzip" } ],"data": {"_events": {},"_readableState": {"highWaterMark": 16384,"buffer": [],"bufferIndex": 0,"length": 0,"pipes": [],"awaitDrainWriters": null },"_writableState": {"highWaterMark": 16384,"length": 0,"corked": 0,"writelen": 0,"bufferedIndex": 0,"pendingcb": 0 },"allowHalfOpen": true,"_eventsCount": 2 },"headers": {"x-goog-api-client": "gdcl/7.2.0 gl-node/20.18.3","content-type": "multipart/related; boundary=a34455bc-c02c-4c89-896a-2b1b530bf92b","Accept-Encoding": "gzip","User-Agent": "google-api-nodejs-client/7.2.0 (gzip)","Authorization": "Bearer <omit>" },"params": {"part": ["id","snippet","status" ],"uploadType": "multipart" },"retry": true,"body": {"_events": {},"_readableState": {"highWaterMark": 16384,"buffer": [],"bufferIndex": 0,"length": 0,"pipes": [],"awaitDrainWriters": null },"_writableState": {"highWaterMark": 16384,"length": 0,"corked": 0,"writelen": 0,"bufferedIndex": 0,"pendingcb": 0 },"allowHalfOpen": true,"_eventsCount": 2 },"responseType": "unknown" },"data": {"kind": "youtube#video","etag": "yWFiu0ADCdhaQtb1HoXEH_pDoeA","id": "YlDM_luRQLE","snippet": {"publishedAt": "2025-04-10T15:53:11Z","channelId": "<omitted>","title": "<omitted>","description": "<omitted>","thumbnails": {"default": {"url": "https://i9.ytimg.com/vi/YlDM_luRQLE/default.jpg?sqp=CKjR378G&rs=AOn4CLCjq6jG8VlCvegcjsZPuGm7PgqTsw","width": 120,"height": 90 },"medium": {"url": "https://i9.ytimg.com/vi/YlDM_luRQLE/mqdefault.jpg?sqp=CKjR378G&rs=AOn4CLD564Jj03lC_HZyByhbW9fDSwCQPg","width": 320,"height": 180 },"high": {"url": "https://i9.ytimg.com/vi/YlDM_luRQLE/hqdefault.jpg?sqp=CKjR378G&rs=AOn4CLACHEqSkwjdlrUkujlTdJj7YNf9Vw","width": 480,"height": 360 } },"channelTitle": "<omitted_channel_name>","categoryId": "22","liveBroadcastContent": "none","localized": {"title": "<omitted_title>","description": "<omitted_description>" } },"status": {"uploadStatus": "uploaded","privacyStatus": "private","license": "youtube","embeddable": true,"publicStatsViewable": true,"selfDeclaredMadeForKids": false } },"headers": {"alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000","content-encoding": "gzip","content-type": "application/json; charset=UTF-8","date": "Thu, 10 Apr 2025 15:53:13 GMT","server": "UploadServer","transfer-encoding": "chunked","vary": "Origin, X-Origin, Referer","warning": "214 UploadServer gzipped","x-guploader-response-body-transformations": "gzipped","x-guploader-uploadid": "AKDAyIurvH8jzOBh3gfTTXIYX_GAf11AgQjdT1kByGNdX6kShi-yEsjUYPn0LO27dYbGXLdP" },"status": 200,"statusText": "OK","request": {"responseURL": "https://youtube.googleapis.com/upload/youtube/v3/videos?part=id&part=snippet&part=status&uploadType=multipart" }}What I expected to happen: Once the response is recieved from the API the video should appear fully uploaded on website channel view, but it is stuck at 0% uploaded and is not playable.
It's not 100% reproducible, the first time I tried uploading a video using the above code, it worked. Everytime since, it has not.
Further details:
I am using a refresh token to create the auth object:
function authorizeWithOAuth( refreshToken: string, credentials: ClientCredentials): Auth.OAuth2Client { const clientSecret = credentials.client_secret; const clientId = credentials.client_id; const redirectUrl = credentials.redirect_uris[0]; const oauth2Client = new OAuth2(clientId, clientSecret, redirectUrl); oauth2Client.setCredentials({ refresh_token: refreshToken }); return oauth2Client;}Linked google issue: https://issuetracker.google.com/issues/409959470
