{"record":{"id":"7f92f12d97f9304b","repo":"gitroomhq/postiz-app","slug":"could-not-determine-the-video-size-for-the-youtube","errorCode":null,"errorMessage":"Could not determine the video size for the YouTube upload","messagePattern":"Could not determine the video size for the YouTube upload","errorType":"exception","errorClass":"BadBody","httpStatus":null,"severity":"error","filePath":"libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts","lineNumber":445,"sourceCode":"  private static readonly YOUTUBE_UPLOAD_BATCH_MS = 4 * 60 * 1000;\n\n  // Resolves the total byte size of the media without loading it into memory:\n  // a HEAD request for remote URLs, statSync for local files.\n  private async youtubeMediaSize(path: string): Promise<number> {\n    if (path.indexOf('http') === 0) {\n      // the media path is user-influenced, keep the SSRF-safe dispatcher that\n      // this.fetch applies to every other outbound request. identity encoding\n      // so content-length matches the bytes a later GET actually streams\n      // (fetch transparently decompresses encoded bodies).\n      setHeartbeatDetails(`youtube: media size ${stripQuery(path)}`);\n      const head = await fetch(path, {\n        method: 'HEAD',\n        headers: { 'accept-encoding': 'identity' },\n        dispatcher: getSsrfSafeDispatcher(),\n      } as any);\n      const length = head.headers.get('content-length');\n      if (!length) {\n        throw new BadBody(\n          this.identifier,\n          '{}',\n          '{}',\n          'Could not determine the video size for the YouTube upload'\n        );\n      }\n      return Number(length);\n    }\n\n    return statSync(path).size;\n  }\n\n  // Returns a streaming body for the [start, end] byte range of the media so we\n  // never hold the whole file in memory: a ranged GET for remote URLs, a ranged\n  // read stream for local files.\n  private async youtubeChunkStream(path: string, start: number, end: number) {\n    if (path.indexOf('http') === 0) {\n      // identity encoding so the store keeps content-length and can answer","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/gitroomhq/postiz-app/blob/0f1647f7491a217d43eb5ae7a480484bdf0aff3e/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts#L427-L463","documentation":"During a resumable YouTube upload, the provider issues an HTTP HEAD request against the stored media URL and expects a content-length header so it can compute the video size. If the storage endpoint responds without content-length (or the header is empty), a BadBody error is thrown because the upload byte range cannot be calculated.","triggerScenarios":"Calling the YouTube provider's upload flow (postPending/post of a video post) where the media URL is served by a storage layer that omits content-length on HEAD requests (e.g. chunked/streaming responses, signed URLs stripped of the header, or a redirect to a server that doesn't return it).","commonSituations":"Self-hosted S3-compatible storage (MinIO) or CDNs/proxies that drop content-length; media served with content-encoding/compression making length unknown; expired or misconfigured signed URLs returning an error page without the header.","solutions":["Verify the media URL returns content-length: curl -I on the stored asset URL","If using a proxy/CDN, configure it to pass through content-length on HEAD requests","Ensure uploads store the file size so the origin can serve accurate HEAD metadata","Retry the post after fixing storage so a fresh media URL is generated"],"exampleFix":"// before\nconst length = head.headers.get('content-length');\nif (!length) { throw new BadBody(...); }\n\n// after (fallback: fail fast at upload time by validating the media record)\nconst length = head.headers.get('content-length');\nif (!length) {\n  throw new BadBody(\n    this.identifier, '{}', '{}',\n    'Could not determine the video size for the YouTube upload'\n  );\n}\n// caller-side: assert media.size is present before scheduling the video post","handlingStrategy":"validation","validationCode":"const head = await fetch(mediaUrl, { method: 'HEAD', headers: { 'accept-encoding': 'identity' } });\nif (!head.headers.get('content-length')) {\n  throw new Error('Media URL has no content-length; fix storage before posting');\n}","typeGuard":"const hasContentLength = (h: Headers): boolean => Boolean(h.get('content-length'));","tryCatchPattern":"catch (e) { if (/Could not determine the video size/.test(e.message)) { fixStorageHeaders(); } throw e; }","preventionTips":["Verify storage returns content-length on HEAD before scheduling video posts","Store the media size at upload time and validate it exists","Avoid proxies that strip content-length"],"tags":["youtube","upload","content-length","storage","video"],"backgroundTag":"missing-content-length-header","analyzedSha":"0f1647f7491a217d43eb5ae7a480484bdf0aff3e","analyzedAt":"2026-08-27T12:09:55.020Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}