<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vinny commits</title>
    <description>The latest articles on DEV Community by Vinny commits (@znagroupllccommits).</description>
    <link>https://dev.to/znagroupllccommits</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4104376%2F5c131a5b-149e-4625-a7dc-ac46e77b0d3d.png</url>
      <title>DEV Community: Vinny commits</title>
      <link>https://dev.to/znagroupllccommits</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/znagroupllccommits"/>
    <language>en</language>
    <item>
      <title>YouTube Data API: duration comes back P0D and processingStatus never leaves processing</title>
      <dc:creator>Vinny commits</dc:creator>
      <pubDate>Tue, 01 Sep 2026 12:48:36 +0000</pubDate>
      <link>https://dev.to/znagroupllccommits/youtube-data-api-duration-comes-back-p0d-and-processingstatus-never-leaves-processing-6fo</link>
      <guid>https://dev.to/znagroupllccommits/youtube-data-api-duration-comes-back-p0d-and-processingstatus-never-leaves-processing-6fo</guid>
      <description>&lt;p&gt;I lost about 18 hours to this one and the fix turned out to be boring, so I'm writing it down for the next person who googles the error string.&lt;/p&gt;

&lt;p&gt;Setup: I have an automated pipeline that renders a video and uploads it to YouTube through the Data API v3. No human in the loop past the approval step. The upload call returned fine. The video ID came back. I logged it, marked the job published, and went to bed.&lt;/p&gt;

&lt;p&gt;Next morning the video was still processing. Not "processing, nearly done." Just processing, indefinitely. The Studio page showed the thumbnail and title, so clearly something had arrived. But &lt;code&gt;videos.list&lt;/code&gt; with &lt;code&gt;part=contentDetails,status&lt;/code&gt; gave me this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"contentDetails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P0D"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"uploadStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uploaded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"processingStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"processing"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;P0D&lt;/code&gt; is ISO 8601 for a zero length duration. Zero days, zero everything. The video was 8 minutes 56 seconds.&lt;/p&gt;

&lt;p&gt;It sat there for 18 hours. It never errored. It never finished. Nothing in the API ever told me the file was bad.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;The upload got killed partway through. Network hiccup, my own process, doesn't really matter which. YouTube kept the bytes it had received and accepted the video record anyway. What it was left holding was a truncated MP4 that the transcoder could never parse, so processing never completed and the duration never resolved past zero.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that cost me the time
&lt;/h2&gt;

&lt;p&gt;My pipeline had a check for exactly this. It just checked the wrong field.&lt;/p&gt;

&lt;p&gt;I was reading &lt;code&gt;fileDetails.fileSize&lt;/code&gt; and comparing it against the size of the file on disk. They matched. I concluded the upload was complete. It was not.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fileDetails.fileSize&lt;/code&gt; is not the number of bytes YouTube received. It is the size declared when the resumable session is opened, before a single byte of content is sent. It is your own number handed back to you. It will match your local file whether you uploaded all of it or four percent of it. As a completeness check it is worthless, and it fails in the worst possible direction, which is that it reports success.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check instead
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;contentDetails.duration&lt;/code&gt;. If it parses to a real runtime, the file arrived intact and the transcoder read it. If it is &lt;code&gt;P0D&lt;/code&gt;, the file is broken and it is not going to recover on its own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_is_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video_resource&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;P0D means the transcoder never got a parseable file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;video_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contentDetails&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duration&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P0D&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rough timing check: my re-upload of the same 8m56s file processed in about two minutes. If yours has been sitting at &lt;code&gt;P0D&lt;/code&gt; for an hour, it's not slow. It's dead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery
&lt;/h2&gt;

&lt;p&gt;You cannot repair the broken video in place. Re-upload the file as a new video and set the old ID to private.&lt;/p&gt;

&lt;p&gt;Worth planning for: you get a &lt;strong&gt;new video ID&lt;/strong&gt;. In my case three Shorts had descriptions pointing at the dead ID and were scheduled to go public the next day. I had to rewrite all three before they published. If anything downstream stores that ID, it needs to be updated as part of recovery, not after someone notices.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed
&lt;/h2&gt;

&lt;p&gt;Two things.&lt;/p&gt;

&lt;p&gt;First, I switched from a one shot upload to a chunked resumable one, so an interrupted transfer can resume instead of leaving a corpse behind.&lt;/p&gt;

&lt;p&gt;Second, and this is the one that mattered, the pipeline no longer treats "the API returned a video ID" as proof of anything. It polls &lt;code&gt;contentDetails.duration&lt;/code&gt; and only marks the job complete when a real runtime comes back.&lt;/p&gt;

&lt;p&gt;The general shape of this bug is one I keep running into. I was verifying against a field that gets populated by my own request rather than by the thing I actually wanted to confirm. It looked like verification. It was an echo.&lt;/p&gt;

</description>
      <category>api</category>
      <category>python</category>
      <category>debugging</category>
      <category>googlecloud</category>
    </item>
  </channel>
</rss>
