<?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: nils44344</title>
    <description>The latest articles on DEV Community by nils44344 (@nils44344).</description>
    <link>https://dev.to/nils44344</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%2F3947721%2F24a53287-7cd2-431e-8051-0b9b6651855b.png</url>
      <title>DEV Community: nils44344</title>
      <link>https://dev.to/nils44344</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nils44344"/>
    <language>en</language>
    <item>
      <title>Gemini 2.5 Flash "no longer available to new users" (404)</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Thu, 22 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/nils44344/gemini-25-flash-no-longer-available-to-new-users-404-61n</link>
      <guid>https://dev.to/nils44344/gemini-25-flash-no-longer-available-to-new-users-404-61n</guid>
      <description>&lt;p&gt;&lt;strong&gt;The error:&lt;/strong&gt;&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This model models/gemini-2.5-flash is no longer available to new users. Please update your code to use models/gemini-3.8-flash for the latest features and improvements. We recommend you to use the Interactions API (https://ai.google.dev/gemini-api/docs/get-started)."&lt;/span&gt;&lt;span class="p"&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="s2"&gt;"NOT_FOUND"&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="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;strong&gt;Tested on:&lt;/strong&gt; @google/genai: 2.27.0 · API key: new, free tier (AI Studio) · Node / OS: 24.14.0 / Windows 11 Pro · Date: October 7, 2026&lt;/p&gt;

&lt;p&gt;This isn't your code, and it isn't your key's permissions. Google has retired the 2.x models &lt;strong&gt;for keys and projects that didn't use them before&lt;/strong&gt;. That's why the same code keeps working on a colleague's old key and fails on yours, or works on your laptop and fails in a new deployment with a fresh key.&lt;/p&gt;

&lt;p&gt;I called every older model name with a brand-new key. There are three different messages, depending on how old the model is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Message&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;gemini-2.5-flash&lt;/code&gt;, &lt;code&gt;gemini-2.5-pro&lt;/code&gt;, &lt;code&gt;gemini-2.5-flash-lite&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;404&lt;/code&gt; "no longer available &lt;strong&gt;to new users&lt;/strong&gt;"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;gemini-2.0-flash&lt;/code&gt;, &lt;code&gt;gemini-2.0-flash-lite&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;404&lt;/code&gt; "no longer available" (gone for everyone)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;gemini-1.5-flash&lt;/code&gt;, &lt;code&gt;gemini-1.5-pro&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;404&lt;/code&gt; "is not found for API version v1beta, or is not supported for generateContent"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each 404 for a 2.x model names its own replacement:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You asked for&lt;/th&gt;
&lt;th&gt;The error suggested&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemini-2.5-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.8-flash&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemini-2.5-pro&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.1-pro-preview&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemini-2.5-flash-lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemini-2.0-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.8-flash&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemini-2.0-flash-lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The model list doesn't warn you
&lt;/h2&gt;

&lt;p&gt;If you check &lt;code&gt;ai.models.list()&lt;/code&gt; first, you'll still find &lt;code&gt;gemini-2.5-flash&lt;/code&gt;, &lt;code&gt;gemini-2.5-pro&lt;/code&gt; and &lt;code&gt;gemini-2.5-flash-lite&lt;/code&gt; in it, listed as supporting &lt;code&gt;generateContent&lt;/code&gt;. Then the call 404s. So "it's in the list" doesn't mean your key can use it: only a real call tells you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Change the model name. These worked with the new free key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-3.5-flash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or 'gemini-3.5-flash-lite'&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ &lt;code&gt;gemini-3.5-flash&lt;/code&gt; and &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt; both answered normally with the same key that got the 404.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To stop this happening at the next retirement, you can use the moving alias instead of a version number. &lt;code&gt;gemini-flash-lite-latest&lt;/code&gt; answered, and the response's &lt;code&gt;modelVersion&lt;/code&gt; showed it was serving &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-flash-lite-latest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modelVersion&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// gemini-3.5-flash-lite&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cost of an alias: the model behind it changes without notice, so output and pricing can shift under you. Log &lt;code&gt;modelVersion&lt;/code&gt; so you can see when it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't jump straight to the suggested model
&lt;/h2&gt;

&lt;p&gt;The 404 for &lt;code&gt;gemini-2.5-flash&lt;/code&gt; says to use &lt;code&gt;gemini-3.8-flash&lt;/code&gt;. Every time I tried it that evening, it returned &lt;code&gt;503&lt;/code&gt; "This model is currently experiencing high demand" or timed out, and &lt;code&gt;gemini-flash-latest&lt;/code&gt; did the same. Meanwhile, older models kept answering. The newest model is the busiest one. Test the replacement before you ship it.&lt;/p&gt;

&lt;p&gt;And for the Pro suggestion (&lt;code&gt;gemini-3.1-pro-preview&lt;/code&gt;): on the free tier its quota is &lt;strong&gt;0&lt;/strong&gt;, so switching to it turns your 404 into a 429. That one is covered in &lt;a href="https://nilaykabariya.blog/ai/gemini-api-429-resource-exhausted" rel="noopener noreferrer"&gt;Gemini API 429 RESOURCE_EXHAUSTED: why your limit is 0&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Checking &lt;code&gt;models.list()&lt;/code&gt; before calling.&lt;/strong&gt; The retired 2.5 models are still listed for a key that can't use them.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Using the exact model the error recommends, as is.&lt;/strong&gt; &lt;code&gt;gemini-3.8-flash&lt;/code&gt; returned &lt;code&gt;503&lt;/code&gt; or timed out every time I called it, and the recommended Pro model has no free-tier quota at all.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Same idea, different provider&lt;/strong&gt; Groq retired its Llama models the same way, with a &lt;code&gt;model_not_found&lt;/code&gt; error: &lt;a href="https://nilaykabariya.blog/ai/groq-api-error-413-429-rate-limit" rel="noopener noreferrer"&gt;Groq API error 413, 429 and model_not_found&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How this was tested
&lt;/h2&gt;

&lt;p&gt;A free Google AI Studio key on October 7, 2026 (a new one: it got the "to new users" 404), with &lt;code&gt;@google/genai&lt;/code&gt; 2.27.0 on Node 24.14.0 (Windows 11). Every model was called once with a one-word prompt and &lt;code&gt;maxOutputTokens: 1&lt;/code&gt;. The model list came from &lt;code&gt;ai.models.list()&lt;/code&gt; with the same key. I didn't have an older key to confirm that the 2.5 models still work for existing users. That part is what the error message itself says ("to new users"), not something I saw. Google retires models on its own schedule, so check the date above.&lt;/p&gt;

&lt;p&gt;The code for every case above is public, so you can run it yourself: &lt;a href="https://github.com/nils44344/nk-repro/tree/main/gemini-429" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-429&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/nils44344/nk-repro" rel="noopener noreferrer"&gt;nk-repro&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published, with updates as new versions ship, at &lt;a href="https://nilaykabariya.blog/ai/gemini-2-5-flash-no-longer-available-to-new-users" rel="noopener noreferrer"&gt;nilaykabariya.blog&lt;/a&gt;. Written with AI assistance; every error and fix was reproduced on a real machine before it was written up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>ai</category>
      <category>api</category>
      <category>node</category>
    </item>
    <item>
      <title>Prisma P1001: Can't reach database server at localhost:5432</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/nils44344/prisma-p1001-cant-reach-database-server-at-localhost5432-5c5o</link>
      <guid>https://dev.to/nils44344/prisma-p1001-cant-reach-database-server-at-localhost5432-5c5o</guid>
      <description>&lt;p&gt;&lt;strong&gt;The error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: P1001: Can't reach database server at `localhost:5432`

Please make sure your database server is running at `localhost:5432`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tested on:&lt;/strong&gt; prisma: 6.19.3 and 7.10.0 · Postgres: 17 (Docker), Supabase CLI 2.120.0 local · Docker: Desktop 4.94.0, node:24-alpine · Node / OS: 24.14.0 / Windows 11 Pro&lt;/p&gt;

&lt;p&gt;P1001 means Prisma opened no connection at all. It never got as far as your password or your database name. The trouble is that it prints exactly the same sentence whatever the reason. I triggered it five different ways, and the message only ever changed in the host and port:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What was wrong&lt;/th&gt;
&lt;th&gt;What Prisma 6 said&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Nothing listening on that port (database stopped, or wrong port)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;P1001: Can't reach database server at `localhost:5432`&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host name doesn't exist on this machine (&lt;code&gt;postgres&lt;/code&gt;, a Compose service)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;P1001: Can't reach database server at `postgres:5432`&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host exists but never answers (firewall, wrong IP)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;P1001: Can't reach database server at `10.255.255.1:5432`&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App inside a container using &lt;code&gt;localhost&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;P1001: Can't reach database server at `localhost:5432`&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Something &lt;em&gt;is&lt;/em&gt; listening, but it isn't Postgres (Supabase's API port)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;P1001: Can't reach database server at `127.0.0.1:54321`&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 1: find out which one you have
&lt;/h2&gt;

&lt;p&gt;Ask the network directly, with the same host and port as in your error. Node is already installed, so this works on any OS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"const s=require('net').connect(5432,'localhost');s.setTimeout(5000);s.on('connect',()=&amp;gt;{console.log('open: something is listening');s.destroy()}).on('timeout',()=&amp;gt;{console.log('timeout: no answer at all');s.destroy()}).on('error',e=&amp;gt;console.log(e.code))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put your host and port into &lt;code&gt;connect(5432,'localhost')&lt;/code&gt;. What it printed for each case:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Go to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ECONNREFUSED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The machine is there, nothing is listening on that port&lt;/td&gt;
&lt;td&gt;Nothing listening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ENOTFOUND&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The host name doesn't resolve from where you ran it&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeout: no answer at all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Packets go nowhere: firewall, wrong IP, or IPv6&lt;/td&gt;
&lt;td&gt;Timeouts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;open: something is listening&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A server is there. Check it's Postgres on that port&lt;/td&gt;
&lt;td&gt;Wrong service&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Run it from the &lt;strong&gt;same place your app runs&lt;/strong&gt;: inside the container if the app is in a container. Otherwise you're testing a different network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nothing is listening on that port
&lt;/h2&gt;

&lt;p&gt;With my Postgres container stopped, and separately with the right server but the default port &lt;code&gt;5432&lt;/code&gt; (mine was mapped to &lt;code&gt;55432&lt;/code&gt;), Prisma gave the P1001 above in both cases. The quick check said &lt;code&gt;ECONNREFUSED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Start the database, and make the port in &lt;code&gt;DATABASE_URL&lt;/code&gt; match the one it really listens on. For Docker, the &lt;strong&gt;left&lt;/strong&gt; side of the port mapping is the one your machine uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker ps --format "{{.Names}} {{.Ports}}"
nk-pg 0.0.0.0:55432-&amp;gt;5432/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"postgresql://postgres:password@localhost:55432/postgres"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ With the container running and port 55432 in the URL, &lt;code&gt;prisma db push&lt;/code&gt; reached the database and synced the schema.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker: localhost and service names
&lt;/h2&gt;

&lt;p&gt;Most P1001 reports involve Docker, because "localhost" means a different machine depending on where the code runs. I ran Prisma inside a &lt;code&gt;node:24-alpine&lt;/code&gt; container on the same Docker network as Postgres (service name &lt;code&gt;postgres&lt;/code&gt;, container port &lt;code&gt;5432&lt;/code&gt;, mapped to &lt;code&gt;55432&lt;/code&gt; on the host):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;From inside the app container&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;localhost:5432&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;P1001: the container's own localhost has no Postgres&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;localhost:55432&lt;/code&gt; (the port mapped on the host)&lt;/td&gt;
&lt;td&gt;P1001&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;postgres:55432&lt;/code&gt; (service name + host port)&lt;/td&gt;
&lt;td&gt;P1001: wrong side of the mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;postgres:5432&lt;/code&gt; (service name + container port)&lt;/td&gt;
&lt;td&gt;✅ in sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;host.docker.internal:55432&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ in sync (Docker Desktop)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And the reverse mistake: running &lt;code&gt;npx prisma migrate&lt;/code&gt; &lt;strong&gt;on your machine&lt;/strong&gt; with the URL from your Compose file. &lt;code&gt;postgres&lt;/code&gt; only exists inside Docker's network, so from Windows I got &lt;code&gt;P1001: Can't reach database server at `postgres:5432`&lt;/code&gt; (the quick check said &lt;code&gt;ENOTFOUND&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Use the address for where the code runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# app running in a container on the same Compose network&lt;/span&gt;
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"postgresql://postgres:password@postgres:5432/postgres"&lt;/span&gt;

&lt;span class="c"&gt;# prisma CLI running on your machine&lt;/span&gt;
&lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"postgresql://postgres:password@localhost:55432/postgres"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Service name with the &lt;strong&gt;container&lt;/strong&gt; port inside Docker; localhost with the &lt;strong&gt;host&lt;/strong&gt; port outside it. If you run migrations from both places, they need different URLs. Pass the right one per command, or keep two &lt;code&gt;.env&lt;/code&gt; files.&lt;/p&gt;

&lt;h2&gt;
  
  
  No answer at all: firewalls, IPs and Supabase
&lt;/h2&gt;

&lt;p&gt;When nothing answers at all, the quick check prints &lt;code&gt;timeout&lt;/code&gt;. Prisma 6's CLI failed about as fast as it did for a closed port, but a Prisma 7 app waited about 30 seconds first (see the Prisma 7 table below). Usually it's a firewall or security group not allowing your IP, a VPN, or simply the wrong IP.&lt;/p&gt;

&lt;p&gt;For hosted Supabase there's a common version of this that I couldn't reproduce here (it needs a hosted project). Per &lt;a href="https://supabase.com/docs/guides/database/connecting-to-postgres" rel="noopener noreferrer"&gt;Supabase's connection docs&lt;/a&gt;, the direct host &lt;code&gt;db.&amp;lt;project&amp;gt;.supabase.co&lt;/code&gt; is IPv6 unless you pay for the IPv4 add-on. On an IPv4-only network (many home ISPs, some CI runners and hosts) it can't be reached at all.&lt;/p&gt;

&lt;p&gt;For Supabase from an IPv4-only network, use the &lt;strong&gt;Session pooler&lt;/strong&gt; connection string from the dashboard's Connect panel (&lt;code&gt;aws-…pooler.supabase.com:5432&lt;/code&gt;) instead of the direct &lt;code&gt;db.&amp;lt;project&amp;gt;.supabase.co&lt;/code&gt; one. If you use the &lt;strong&gt;transaction&lt;/strong&gt; pooler (port &lt;code&gt;6543&lt;/code&gt;) with Prisma, Supabase says to add &lt;code&gt;?pgbouncer=true&lt;/code&gt;. Neither of these was tested here: no hosted project.&lt;/p&gt;

&lt;p&gt;Once Prisma connects, Supabase's API can still refuse your writes with a different code. That one is &lt;a href="https://nilaykabariya.blog/database/supabase-error-42501-row-level-security" rel="noopener noreferrer"&gt;Supabase error 42501&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Something is listening, but it isn't Postgres
&lt;/h2&gt;

&lt;p&gt;The local Supabase stack runs the database on &lt;code&gt;54322&lt;/code&gt; and its HTTP API on &lt;code&gt;54321&lt;/code&gt;. Pointing Prisma at &lt;code&gt;54321&lt;/code&gt; didn't give a protocol error. It gave the same P1001, even though the port was open and answering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: P1001: Can't reach database server at `127.0.0.1:54321`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Port &lt;code&gt;54322&lt;/code&gt; worked. If the quick check says &lt;code&gt;open&lt;/code&gt; but Prisma says P1001, compare your port with what's actually serving Postgres.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prisma 7 doesn't always say P1001
&lt;/h2&gt;

&lt;p&gt;In Prisma 7, the CLI (&lt;code&gt;prisma migrate dev&lt;/code&gt;) still prints P1001. But in your app, which talks to the database through a driver adapter such as &lt;code&gt;@prisma/adapter-pg&lt;/code&gt;, the same problems produced three different errors:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Prisma 7 app error&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database stopped&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PrismaClientKnownRequestError&lt;/code&gt;, &lt;code&gt;code: 'ECONNREFUSED'&lt;/code&gt;, and a message with &lt;strong&gt;no reason in it&lt;/strong&gt;, just the invocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Host name doesn't resolve&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;code: 'P1001'&lt;/code&gt;, "Can't reach database server at postgres", &lt;code&gt;meta.driverAdapterError.cause.kind: 'DatabaseNotReachable'&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unreachable IP&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;code: 'P1008'&lt;/code&gt;, "Operation has timed out", &lt;code&gt;kind: 'SocketTimeout'&lt;/code&gt;, after about 30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So if you only grep logs for P1001 on Prisma 7, you'll miss the stopped database entirely. Prisma 6's client, for comparison, threw &lt;code&gt;PrismaClientInitializationError&lt;/code&gt; with the same "Can't reach database server" text as the CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Changing the password or the database name.&lt;/strong&gt; P1001 happens before Prisma ever sends them. In all five cases here, the credentials were never checked.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Using the host-side port inside Docker.&lt;/strong&gt; &lt;code&gt;postgres:55432&lt;/code&gt; and &lt;code&gt;localhost:55432&lt;/code&gt; both failed inside the container. The host port only exists on the host.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Trusting "something is listening".&lt;/strong&gt; Supabase's API port was open and still gave P1001. An open port isn't necessarily Postgres.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How this was tested
&lt;/h2&gt;

&lt;p&gt;Postgres 17 in Docker Desktop 4.94.0 on Windows 11, mapped to port 55432, with Prisma 6.19.3 (&lt;code&gt;prisma db push&lt;/code&gt; and a client query) and Prisma 7.10.0 (&lt;code&gt;prisma migrate dev&lt;/code&gt;, plus queries through &lt;code&gt;@prisma/adapter-pg&lt;/code&gt;). Each failure was produced on purpose: container stopped, wrong port, Compose host name used from Windows, a non-routable IP (&lt;code&gt;10.255.255.1&lt;/code&gt;), and the API port of a local Supabase stack (CLI 2.120.0). The container cases ran in &lt;code&gt;node:24-alpine&lt;/code&gt; on a user-defined Docker network with Postgres aliased as &lt;code&gt;postgres&lt;/code&gt;. Hosted Supabase (IPv6, poolers) wasn't tested: those notes come from Supabase's documentation, linked above.&lt;/p&gt;

&lt;p&gt;The code for every case above is public, so you can run it yourself: &lt;a href="https://github.com/nils44344/nk-repro/tree/main/prisma6-noinit" rel="noopener noreferrer"&gt;&lt;code&gt;prisma6-noinit&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/nils44344/nk-repro/tree/main/prisma-init" rel="noopener noreferrer"&gt;&lt;code&gt;prisma-init&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/nils44344/nk-repro" rel="noopener noreferrer"&gt;nk-repro&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published, with updates as new versions ship, at &lt;a href="https://nilaykabariya.blog/database/prisma-p1001-cant-reach-database-server" rel="noopener noreferrer"&gt;nilaykabariya.blog&lt;/a&gt;. Written with AI assistance; every error and fix was reproduced on a real machine before it was written up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>prisma</category>
      <category>postgres</category>
      <category>docker</category>
      <category>database</category>
    </item>
    <item>
      <title>Gemini API 429 RESOURCE_EXHAUSTED: why your limit is 0</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Fri, 16 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/nils44344/gemini-api-429-resourceexhausted-why-your-limit-is-0-68o</link>
      <guid>https://dev.to/nils44344/gemini-api-429-resourceexhausted-why-your-limit-is-0-68o</guid>
      <description>&lt;p&gt;&lt;strong&gt;The error:&lt;/strong&gt;&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You exceeded your current quota, please check your plan and billing details. ...
* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 0, model: gemini-3.1-pro
Please retry in 10h17m5.723541104s."&lt;/span&gt;&lt;span class="p"&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="s2"&gt;"RESOURCE_EXHAUSTED"&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="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;strong&gt;Tested on:&lt;/strong&gt; @google/genai: 2.27.0 · API tier: Free (AI Studio key) · Node / OS: 24.14.0 / Windows 11 Pro · Date: October 7, 2026&lt;/p&gt;

&lt;p&gt;Every Gemini quota error looks the same at first: HTTP &lt;code&gt;429&lt;/code&gt;, &lt;code&gt;"status": "RESOURCE_EXHAUSTED"&lt;/code&gt;, "You exceeded your current quota". The part that matters is one line further down, in the &lt;code&gt;* Quota exceeded for metric&lt;/code&gt; lines: &lt;strong&gt;the limit number and the model&lt;/strong&gt;. With a free AI Studio key I hit three different ones on purpose, and each one needs a different fix:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The message says&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;th&gt;Retry delay it gave me&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit: 0, model: gemini-3.1-pro&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your tier has &lt;strong&gt;no&lt;/strong&gt; quota for this model&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;10h17m&lt;/code&gt; (waiting doesn't help)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit: 5, model: gemini-3.5-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests &lt;strong&gt;per minute&lt;/strong&gt; used up&lt;/td&gt;
&lt;td&gt;&lt;code&gt;46s&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;limit: 20, model: gemini-3.5-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests &lt;strong&gt;per day&lt;/strong&gt; used up&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;9h54m&lt;/code&gt;, until 00:00 UTC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;details&lt;/code&gt; array names the quota exactly. The per-minute one was &lt;code&gt;GenerateRequestsPerMinutePerProjectPerModel-FreeTier&lt;/code&gt;, the daily one &lt;code&gt;GenerateRequestsPerDayPerProjectPerModel-FreeTier&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;limit: 0&lt;/code&gt;: the model isn't in your tier
&lt;/h2&gt;

&lt;p&gt;This was the first call I made with the key. It wasn't a burst: a single request to &lt;code&gt;gemini-3.1-pro-preview&lt;/code&gt; came back &lt;code&gt;429&lt;/code&gt; straight away. Same for &lt;code&gt;gemini-pro-latest&lt;/code&gt;. On the free tier these models have a quota of &lt;strong&gt;zero&lt;/strong&gt;. The error still says "Please retry in 10h17m", but that only marks when the daily counters reset. Retrying at that time gets the same &lt;code&gt;limit: 0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use a model that your tier has quota for. With this free key, &lt;code&gt;gemini-3.5-flash&lt;/code&gt;, &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt; and &lt;code&gt;gemini-flash-latest&lt;/code&gt; answered normally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gemini-3.5-flash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need the Pro model specifically, that means a paid tier (billing turned on for the Google Cloud project behind the key). I didn't test that: it costs money, and it's the only way past a &lt;code&gt;limit: 0&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Per-minute limit: wait the time it gives you
&lt;/h2&gt;

&lt;p&gt;Sending one tiny request after another to &lt;code&gt;gemini-3.5-flash&lt;/code&gt;, calls 1 to 7 succeeded, and call 8 failed &lt;strong&gt;8.4 seconds in&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_free_tier_requests, limit: 5, model: gemini-3.5-flash
Please retry in 46.130728812s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The limit says 5, but seven got through first, so don't use the number to pace yourself exactly. The &lt;code&gt;@google/genai&lt;/code&gt; SDK (2.27.0) threw this straight to my code: it didn't retry on its own.&lt;/p&gt;

&lt;p&gt;Wait the &lt;code&gt;retryDelay&lt;/code&gt; from the error's &lt;code&gt;RetryInfo&lt;/code&gt; detail, then retry. But cap the wait, because a daily limit sends a delay of hours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApiError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@google/genai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxWaits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;ApiError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;maxWaits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// "high demand": back off and try again&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/limit: 0&lt;/span&gt;&lt;span class="se"&gt;\b&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// no quota for this model at all&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;RetryInfo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))?.&lt;/span&gt;&lt;span class="nx"&gt;retryDelay&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// a daily quota: the delay runs to midnight UTC&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ After the per-minute 429, waiting 50 seconds and calling again succeeded. With the helper, a &lt;code&gt;limit: 0&lt;/code&gt; model and a used-up daily quota both failed at once instead of hanging, which is what you want: neither one recovers by waiting a minute.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;The SDK's retryOptions won't cover this&lt;/strong&gt; &lt;code&gt;@google/genai&lt;/code&gt; has &lt;code&gt;httpOptions.retryOptions&lt;/code&gt; (attempts, backoff starting at 1 second, up to 60). Exponential backoff guesses at the wait. The error already tells you the exact delay, and for a daily limit no amount of backoff is long enough.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Daily limit: it resets at midnight UTC, not Pacific time
&lt;/h2&gt;

&lt;p&gt;My test calls used up &lt;code&gt;gemini-3.5-flash&lt;/code&gt;'s free daily quota (&lt;code&gt;limit: 20&lt;/code&gt;). The retry delay pointed at &lt;strong&gt;2026-10-08 00:00:13 UTC&lt;/strong&gt;. That's 8 PM in New York and 5 PM in San Francisco, not midnight Pacific as a lot of older answers say.&lt;/p&gt;

&lt;p&gt;Switch to a different model until the reset. Each model has its own counters: right after &lt;code&gt;gemini-3.5-flash&lt;/code&gt; hit its daily limit, nine calls in a row to &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt; all succeeded.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 The quota IDs end in &lt;code&gt;PerProjectPerModel&lt;/code&gt;: the counters belong to the Google Cloud project behind the key, and to one model. From that wording, a second key in the same project would share them. I didn't create one to check.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Not a 429: "This model is currently experiencing high demand"
&lt;/h2&gt;

&lt;p&gt;While I was testing, &lt;code&gt;gemini-3.8-flash&lt;/code&gt; (the newest Flash) returned this on every call, and later &lt;code&gt;gemini-3.5-flash&lt;/code&gt; did too, for a few calls:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&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;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;503&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This model is currently experiencing high demand. Spikes in demand are usually temporary. Please try again later."&lt;/span&gt;&lt;span class="p"&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="s2"&gt;"UNAVAILABLE"&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="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;This is Google being overloaded, not your quota. There's no retry delay in it, and other models kept working at the same moment. Calls that did go through during that period were slow, too: one took 56 seconds.&lt;/p&gt;

&lt;p&gt;Back off and retry, or use another model for a while. The helper above waits 2, 4, then 8 seconds. In my run the 503s stopped after those three waits (the next answer was the daily-limit 429, because by then my test calls had used up the day's quota).&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Waiting out the "retry in 10h" on a &lt;code&gt;limit: 0&lt;/code&gt; model.&lt;/strong&gt; That delay only points to the daily reset. The quota for that model on the free tier is zero, so it's zero again after the reset.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Trusting the limit number to pace requests.&lt;/strong&gt; With &lt;code&gt;limit: 5&lt;/code&gt; per minute, seven requests got through before the 429. Pace from the 429 and its delay, not from the number.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Retrying a daily limit with backoff.&lt;/strong&gt; My first helper honoured the delay with no cap and went to sleep for 36,030 seconds. If a delay is longer than a couple of minutes, it's a daily quota: stop and switch model.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Gemini says the model is gone?&lt;/strong&gt; With a new key, &lt;code&gt;gemini-2.5-flash&lt;/code&gt; and &lt;code&gt;gemini-2.5-pro&lt;/code&gt; didn't return a 429 at all. They returned &lt;code&gt;404&lt;/code&gt; "no longer available to new users". That's a different fix (the model name), not your quota.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How this was tested
&lt;/h2&gt;

&lt;p&gt;A free Google AI Studio API key on October 7, 2026, with &lt;code&gt;@google/genai&lt;/code&gt; 2.27.0 on Node 24.14.0 (Windows 11). Every request was one word with &lt;code&gt;maxOutputTokens: 1&lt;/code&gt;, sent one after another, never in parallel. Limits were reached on purpose: a Pro model for &lt;code&gt;limit: 0&lt;/code&gt;, a fast loop for the per-minute limit, and the day's test calls for the daily one. The limits shown are the free tier's on that day. Google changes them, so read the numbers in your own error message. I didn't test paid tiers, Vertex AI, or the Gemini CLI, whose 429s come from a different quota.&lt;/p&gt;

&lt;p&gt;The code for every case above is public, so you can run it yourself: &lt;a href="https://github.com/nils44344/nk-repro/tree/main/gemini-429" rel="noopener noreferrer"&gt;&lt;code&gt;gemini-429&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/nils44344/nk-repro" rel="noopener noreferrer"&gt;nk-repro&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published, with updates as new versions ship, at &lt;a href="https://nilaykabariya.blog/ai/gemini-api-429-resource-exhausted" rel="noopener noreferrer"&gt;nilaykabariya.blog&lt;/a&gt;. Written with AI assistance; every error and fix was reproduced on a real machine before it was written up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>ai</category>
      <category>api</category>
      <category>node</category>
    </item>
    <item>
      <title>Supabase error 42501: new row violates row-level security policy</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Tue, 13 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/nils44344/supabase-error-42501-new-row-violates-row-level-security-policy-pch</link>
      <guid>https://dev.to/nils44344/supabase-error-42501-new-row-violates-row-level-security-policy-pch</guid>
      <description>&lt;p&gt;&lt;strong&gt;The error:&lt;/strong&gt;&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;code:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="mi"&gt;42501&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;details:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;hint:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;message:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;row&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;violates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;row-level&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;security&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;policy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;table&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"profiles"&lt;/span&gt;&lt;span class="err"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tested on:&lt;/strong&gt; supabase-js: 2.117.2 · Supabase CLI: 2.120.0 (local stack) · Postgres: 17.11 · PostgREST: 16.4 · Node / OS: 24.14.0 / Windows 11 Pro&lt;/p&gt;

&lt;p&gt;&lt;code&gt;42501&lt;/code&gt; is Postgres's code for "insufficient privilege". Supabase returns it in two very different situations, and the message tells you which one you're in:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Message&lt;/th&gt;
&lt;th&gt;What blocked the request&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new row violates row-level security policy for table "x"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A row-level security (RLS) &lt;strong&gt;policy&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;401&lt;/code&gt; signed out, &lt;code&gt;403&lt;/code&gt; signed in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;new row violates row-level security policy (USING expression) for table "x"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The same, on an upsert that hit an existing row&lt;/td&gt;
&lt;td&gt;&lt;code&gt;403&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;new row violates row-level security policy&lt;/code&gt; (no table name)&lt;/td&gt;
&lt;td&gt;A &lt;strong&gt;Storage&lt;/strong&gt; upload&lt;/td&gt;
&lt;td&gt;&lt;code&gt;statusCode: "403"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;permission denied for table x&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A missing table &lt;strong&gt;GRANT&lt;/strong&gt; (not a policy)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;401&lt;/code&gt; / &lt;code&gt;403&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The first three are about policies. The last one is about grants, and it's the one that will suddenly show up on existing projects after &lt;strong&gt;October 30, 2026&lt;/strong&gt; (see the last section). Adding a policy does nothing for it, and I tested that.&lt;/p&gt;

&lt;p&gt;I reproduced every row of that table on a local Supabase stack, with &lt;code&gt;supabase-js&lt;/code&gt; calling the real API. Each section below starts with what triggered it and ends with the change that made the same call succeed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which policy is missing
&lt;/h2&gt;

&lt;p&gt;Supabase's RLS denies by default, &lt;strong&gt;per operation&lt;/strong&gt;. A table with RLS on and only a SELECT policy can be read but not written. Inserting with the publishable (anon) key returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status: 401 Unauthorized
code: '42501'
message: 'new row violates row-level security policy for table "notes_selectonly"'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same table with &lt;strong&gt;no policies at all&lt;/strong&gt; behaved differently for reads: &lt;code&gt;select()&lt;/code&gt; came back &lt;code&gt;200&lt;/code&gt; with &lt;code&gt;data: []&lt;/code&gt;. No error, just an empty list. So "reads work, writes fail" usually means you wrote a read policy and nothing else.&lt;/p&gt;

&lt;p&gt;Add a policy for the operation that failed. For a table anyone can write to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"anyone can insert"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes_selectonly&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For rows that belong to a user (the usual &lt;code&gt;profiles&lt;/code&gt; case), check the owner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"insert own profile"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profiles&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ With the policy in place, the same insert returned &lt;code&gt;201 Created&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;.insert().select()&lt;/code&gt; needs a SELECT policy too
&lt;/h2&gt;

&lt;p&gt;This one catches people who &lt;em&gt;did&lt;/em&gt; write an INSERT policy. With only an INSERT policy on the table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;insert({ body: 'x' })&lt;/code&gt; returned &lt;code&gt;201 Created&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;insert({ body: 'x' }).select()&lt;/code&gt; returned &lt;code&gt;401&lt;/code&gt; and the same 42501 message.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Asking for the row back means Postgres has to be allowed to &lt;strong&gt;read&lt;/strong&gt; the new row, and without a SELECT policy it isn't. The whole insert is rolled back: I counted the rows afterwards, and the failed call left nothing behind.&lt;/p&gt;

&lt;p&gt;Add a SELECT policy that covers the new row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"anyone can read"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notes_insertonly&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ &lt;code&gt;insert().select()&lt;/code&gt; then returned &lt;code&gt;201&lt;/code&gt; with the new row in &lt;code&gt;data&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  You're not signed in (yet)
&lt;/h2&gt;

&lt;p&gt;A policy written &lt;code&gt;to authenticated&lt;/code&gt; doesn't apply to a request made with the anon key and no session. In my test the &lt;code&gt;profiles&lt;/code&gt; insert failed with &lt;strong&gt;401&lt;/strong&gt; when signed out, and with &lt;strong&gt;403&lt;/strong&gt; when signed in but inserting someone else's &lt;code&gt;id&lt;/code&gt;. Same code, same message, different status. If you see 401, check that the client really has a session at the moment of the insert. One way to end up without one, which I didn't reproduce here: inserting the profile right after &lt;code&gt;signUp()&lt;/code&gt; on a project with email confirmation on, where sign-up doesn't return a session yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;user_id&lt;/code&gt; isn't sent, so the check fails
&lt;/h2&gt;

&lt;p&gt;With a policy like &lt;code&gt;with check ((select auth.uid()) = user_id)&lt;/code&gt;, an insert that leaves &lt;code&gt;user_id&lt;/code&gt; out writes &lt;code&gt;null&lt;/code&gt;, and &lt;code&gt;null = &amp;lt;your id&amp;gt;&lt;/code&gt; is not true. Signed in, &lt;code&gt;insert({ task: 't' })&lt;/code&gt; failed with &lt;code&gt;403&lt;/code&gt; and 42501. Sending the column worked, but the cleaner fix is to stop relying on the client for it.&lt;/p&gt;

&lt;p&gt;Make the database fill it in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;todos&lt;/span&gt; &lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;column&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ The same &lt;code&gt;insert({ task: 't' })&lt;/code&gt;, with no &lt;code&gt;user_id&lt;/code&gt;, then returned &lt;code&gt;201&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Upsert: the "(USING expression)" version
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;upsert()&lt;/code&gt; is an insert that can turn into an update. With INSERT and SELECT policies but no UPDATE policy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first &lt;code&gt;upsert()&lt;/code&gt; (new row) returned &lt;code&gt;201&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The second &lt;code&gt;upsert()&lt;/code&gt; on the same &lt;code&gt;id&lt;/code&gt; returned &lt;code&gt;403&lt;/code&gt; with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new row violates row-level security policy (USING expression) for table "profiles"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Related trap: a plain &lt;code&gt;.update()&lt;/code&gt; with no UPDATE policy did &lt;strong&gt;not&lt;/strong&gt; error at all. It returned &lt;code&gt;200&lt;/code&gt;, changed nothing, and gave back an empty list.&lt;/p&gt;

&lt;p&gt;Add an UPDATE policy. It needs &lt;code&gt;using&lt;/code&gt; (which rows you may touch) and &lt;code&gt;with check&lt;/code&gt; (what they may become):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"update own profile"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;profiles&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ The repeat &lt;code&gt;upsert()&lt;/code&gt; then returned &lt;code&gt;200&lt;/code&gt;, and &lt;code&gt;.update()&lt;/code&gt; returned the changed row.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Storage uploads
&lt;/h2&gt;

&lt;p&gt;Storage keeps its files as rows in &lt;code&gt;storage.objects&lt;/code&gt;, so uploads go through RLS too. A bucket with no policy on that table refused an upload with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;StorageApiError: new row violates row-level security policy
status: 400, statusCode: '403'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no table name in this one, and the HTTP status (400) doesn't match the &lt;code&gt;statusCode&lt;/code&gt; field (403). Once uploads worked, uploading to an existing name with &lt;code&gt;upsert: true&lt;/code&gt; failed again with &lt;code&gt;code: 'AccessDenied'&lt;/code&gt; and the same message. Overwriting replaces an existing row, and an INSERT policy only covers new ones.&lt;/p&gt;

&lt;p&gt;Policies on &lt;code&gt;storage.objects&lt;/code&gt;, scoped to the bucket. Insert for new uploads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"upload avatars"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'avatars'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for &lt;code&gt;upsert: true&lt;/code&gt;, SELECT and UPDATE as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"read avatars"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'avatars'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"overwrite avatars"&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucket_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'avatars'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ With the insert policy, new uploads returned a path and id. With all three, &lt;code&gt;upsert: true&lt;/code&gt; overwrote the file. I added SELECT and UPDATE together, so I can't say whether UPDATE alone is enough.&lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;Use authenticated, not anon, in a real app&lt;/strong&gt; I used &lt;code&gt;anon&lt;/code&gt; so the test didn't need a login. In production, scope uploads to &lt;code&gt;authenticated&lt;/code&gt;, and usually to the user's own folder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  "permission denied for table": the October 30 change
&lt;/h2&gt;

&lt;p&gt;Supabase's API used to give &lt;code&gt;anon&lt;/code&gt;, &lt;code&gt;authenticated&lt;/code&gt; and &lt;code&gt;service_role&lt;/code&gt; full access to every new table in &lt;code&gt;public&lt;/code&gt; automatically, leaving RLS to do the filtering. That automatic grant was switched off for &lt;strong&gt;new projects on May 30, 2026&lt;/strong&gt;. Per &lt;a href="https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically" rel="noopener noreferrer"&gt;Supabase's changelog&lt;/a&gt;, it applies to &lt;strong&gt;all existing projects on October 30, 2026&lt;/strong&gt;. Tables that already exist keep their grants; tables you create after that don't get any.&lt;/p&gt;

&lt;p&gt;The local CLI has the same switch (&lt;code&gt;auto_expose_new_tables&lt;/code&gt; in &lt;code&gt;supabase/config.toml&lt;/code&gt;). I turned it off and created an &lt;code&gt;orders&lt;/code&gt; table &lt;strong&gt;with RLS enabled and working policies&lt;/strong&gt;. Every call failed anyway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;status: 401 Unauthorized
code: '42501'
message: 'permission denied for table orders'
hint: 'Grant the required privileges to the current role with: …'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things surprised me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Policies don't matter here.&lt;/strong&gt; Postgres checks table privileges before it ever looks at RLS. With the grants added and the INSERT policy dropped, the error switched to the RLS message. So the order is: grant first, then policy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;service_role&lt;/code&gt; fails too.&lt;/strong&gt; The service key bypasses RLS, but not grants. A server-side &lt;code&gt;select()&lt;/code&gt; with the service key returned &lt;code&gt;403&lt;/code&gt;, with a hint naming the privilege it lacked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;hint&lt;/code&gt; is worth reading: it names the exact privileges that call needed. For the anon read it was &lt;code&gt;GRANT SELECT ON public.orders TO anon;&lt;/code&gt;, and &lt;code&gt;insert().select()&lt;/code&gt; as a signed-in user asked for &lt;code&gt;GRANT SELECT, INSERT ON public.orders TO authenticated;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Grant the API roles access to the new table, then keep RLS on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;anon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;service_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ All four calls (anon read, anon insert, service key read, signed-in insert) then succeeded. Only grant &lt;code&gt;anon&lt;/code&gt; what logged-out visitors should really do. I granted it INSERT as well, only so I could run the anon test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you create tables in migrations, put the grants in the same migration as the &lt;code&gt;create table&lt;/code&gt;. Otherwise the table works on your old project and fails on the next one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Disabling RLS.&lt;/strong&gt; It "fixed" every policy error: anon inserts returned &lt;code&gt;201&lt;/code&gt;. It also let the anon key read every row in the table, including rows other users had inserted. The anon key ships in your frontend, so anyone could do the same. It also does nothing for "permission denied for table", which is about grants.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Adding a policy for "permission denied for table".&lt;/strong&gt; The &lt;code&gt;orders&lt;/code&gt; table had SELECT and INSERT policies the whole time. Until the GRANT existed, they were never reached.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Switching the client to the service_role key.&lt;/strong&gt; It bypassed RLS (the no-policy insert returned &lt;code&gt;201&lt;/code&gt;), but it was still blocked by a missing grant. And a service key in browser code gives everyone full access to your database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How this was tested
&lt;/h2&gt;

&lt;p&gt;A local Supabase stack (CLI 2.120.0: Postgres 17.11, PostgREST 16.4, GoTrue 2.197.0, Storage 1.79.36) in Docker Desktop on Windows 11, called from Node 24.14.0 with &lt;code&gt;@supabase/supabase-js&lt;/code&gt; 2.117.2. Each case was a table built in a migration with exactly one thing missing, called with the publishable (anon) key, as a freshly signed-up user, and with the service key. Every fix was applied on its own, and the same call rerun. The "permission denied" cases used &lt;code&gt;auto_expose_new_tables = false&lt;/code&gt; locally, which is the setting Supabase applies to hosted projects. I didn't run them against a hosted project, and I didn't test the GraphQL API, which the same change also affects.&lt;/p&gt;

&lt;p&gt;The code for every case above is public, so you can run it yourself: &lt;a href="https://github.com/nils44344/nk-repro/tree/main/supabase-42501" rel="noopener noreferrer"&gt;&lt;code&gt;supabase-42501&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/nils44344/nk-repro" rel="noopener noreferrer"&gt;nk-repro&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published, with updates as new versions ship, at &lt;a href="https://nilaykabariya.blog/database/supabase-error-42501-row-level-security" rel="noopener noreferrer"&gt;nilaykabariya.blog&lt;/a&gt;. Written with AI assistance; every error and fix was reproduced on a real machine before it was written up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fix: @prisma/client did not initialize yet (Prisma 6 and 7)</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Sat, 10 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/nils44344/fix-prismaclient-did-not-initialize-yet-prisma-6-and-7-13f9</link>
      <guid>https://dev.to/nils44344/fix-prismaclient-did-not-initialize-yet-prisma-6-and-7-13f9</guid>
      <description>&lt;p&gt;&lt;strong&gt;The error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;D:\project\node_modules\.prisma\client\default.js:43
    throw new Error('@prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.');
    ^

Error: @prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.
    at new PrismaClient (D:\project\node_modules\.prisma\client\default.js:43:11)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tested on:&lt;/strong&gt; prisma / @prisma/client: 6.19.3 and 7.10.0 · npm: 11.9.0, 11.19.0, 12.2.0 · Node: 24.14.0 (Windows), 24.21.0 (Ubuntu, WSL) · OS: Windows 11 Pro + Ubuntu 24.04&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@prisma/client&lt;/code&gt; doesn't contain your client. It contains a &lt;strong&gt;placeholder&lt;/strong&gt; that throws this error until &lt;code&gt;prisma generate&lt;/code&gt; writes the real one, built from your &lt;code&gt;schema.prisma&lt;/code&gt;. So the message is literally true: generate hasn't run, or didn't finish, in the copy of &lt;code&gt;node_modules&lt;/code&gt; your app is loading.&lt;/p&gt;

&lt;p&gt;The confusing part is that &lt;code&gt;npm install&lt;/code&gt; is supposed to run it for you. I reproduced four situations where it doesn't, plus what changed in Prisma 7, where you don't get this message at all:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Message you get&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install didn't generate (Windows, or schema added after install)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@prisma/client did not initialize yet&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm ci --ignore-scripts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Cannot find module '.prisma/client/default'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npm 12 blocked Prisma's install script&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Cannot find module '.prisma/client/default'&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prisma 7, generate never ran&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Named export 'PrismaClient' not found&lt;/code&gt; (ESM), &lt;code&gt;does not provide an export named 'PrismaClient'&lt;/code&gt; (TypeScript), or &lt;code&gt;Cannot find module '.prisma/client/default'&lt;/code&gt; (CommonJS)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Run it once to unblock yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Generated Prisma Client (v6.19.3) to .\node_modules\@prisma\client in 215ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then stop depending on the install doing it. Add it to your own scripts in &lt;code&gt;package.json&lt;/code&gt;:&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;"scripts"&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;"postinstall"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prisma generate"&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;blockquote&gt;
&lt;p&gt;✅ With that script, a clean &lt;code&gt;npm ci&lt;/code&gt; printed &lt;code&gt;✔ Generated Prisma Client&lt;/code&gt; and the app started, on Windows (npm 11.9.0) and on Linux with npm 12.2.0, which blocks Prisma's own install script.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A project's own &lt;code&gt;postinstall&lt;/code&gt; is a different thing from a dependency's install script. That's why the line above keeps working on npm 12, where Prisma's script is blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  On Windows, the install didn't generate for me
&lt;/h2&gt;

&lt;p&gt;Same &lt;code&gt;package.json&lt;/code&gt;, same lockfile, same &lt;code&gt;prisma/schema.prisma&lt;/code&gt;, Prisma 6.19.3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On &lt;strong&gt;Ubuntu&lt;/strong&gt; (WSL, npm 11.19.0), &lt;code&gt;npm ci&lt;/code&gt; printed &lt;code&gt;✔ Generated Prisma Client (v6.19.3)&lt;/code&gt; during the install, and the app ran.&lt;/li&gt;
&lt;li&gt;On &lt;strong&gt;Windows 11&lt;/strong&gt; (npm 11.9.0), &lt;code&gt;npm ci&lt;/code&gt; ran &lt;code&gt;@prisma/client&lt;/code&gt;'s postinstall without any error, generated nothing, and the app threw &lt;code&gt;did not initialize yet&lt;/code&gt;. It happened from PowerShell and from Git Bash, and running &lt;code&gt;npm rebuild @prisma/client&lt;/code&gt; changed nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The script is written to fail silently. It looks for the Prisma CLI by resolving the &lt;code&gt;prisma&lt;/code&gt; package, and Prisma 6's package points its entry at &lt;code&gt;./build/types.js&lt;/code&gt;, a file that isn't in the published package (I checked 6.6.0 through 6.19.3). So that lookup always fails, and the script falls back to running &lt;code&gt;prisma -v&lt;/code&gt; on the PATH. That evidently worked on Linux and not on this Windows machine. I didn't trace that last step further, but the fix above doesn't depend on it.&lt;/p&gt;

&lt;p&gt;The same thing happens on any OS if you add &lt;code&gt;schema.prisma&lt;/code&gt; &lt;em&gt;after&lt;/em&gt; installing, because the install already ran with no schema to generate from.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;npm ci --ignore-scripts&lt;/code&gt; (and CI templates that use it)
&lt;/h2&gt;

&lt;p&gt;Skipping install scripts skips Prisma's too, so not even the placeholder gets written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Cannot find module '.prisma/client/default'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same cause, different message. The project-level &lt;code&gt;postinstall&lt;/code&gt; doesn't help here either, because &lt;code&gt;--ignore-scripts&lt;/code&gt; skips it too.&lt;/p&gt;

&lt;p&gt;When you install with &lt;code&gt;--ignore-scripts&lt;/code&gt;, generate as its own build step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ci &lt;span class="nt"&gt;--ignore-scripts&lt;/span&gt;
npx prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  npm 12 blocks Prisma's install script
&lt;/h2&gt;

&lt;p&gt;npm 12 doesn't run dependencies' install scripts unless you approve them (more on that in &lt;a href="https://nilaykabariya.blog/tested/npm-12-breaking-changes-tested" rel="noopener noreferrer"&gt;npm 12 breaking changes, tested&lt;/a&gt;). On Ubuntu, a clean &lt;code&gt;npm ci&lt;/code&gt; with npm 12.2.0 warned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm warn install-scripts 3 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   @prisma/client@6.19.3 (postinstall: node scripts/postinstall.js)
npm warn install-scripts   @prisma/engines@6.19.3 (postinstall: node scripts/postinstall.js)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app then failed with &lt;code&gt;Cannot find module '.prisma/client/default'&lt;/code&gt;. The &lt;code&gt;postinstall&lt;/code&gt; in the fix solves it. If you'd rather let Prisma's own script run, approve it by name:&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;"allowScripts"&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;"@prisma/client"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@prisma/engines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prisma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;blockquote&gt;
&lt;p&gt;✅ With these three approved, npm 12 printed no warning and the app started.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Don't add it with npm pkg set&lt;/strong&gt; I first tried &lt;code&gt;npm pkg set "allowScripts.@prisma/client@6.19.3"=true&lt;/code&gt;. npm split the key on its dots, and npm 12 then ignored it with &lt;code&gt;semver ranges (^, ~, &amp;gt;=, &amp;lt;) are not allowed&lt;/code&gt;. Edit &lt;code&gt;package.json&lt;/code&gt; by hand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;npm i prisma&lt;/code&gt; gave you Prisma 8
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;npx prisma generate&lt;/code&gt; itself fails with &lt;code&gt;No command registered for `generate`&lt;/code&gt;, the CLI you installed is the Prisma 8 release candidate, which npm currently serves as &lt;code&gt;prisma@latest&lt;/code&gt;. Generate never runs, so you end up here. That one has its own fix: &lt;a href="https://nilaykabariya.blog/database/prisma-generate-no-command-registered-prisma-8" rel="noopener noreferrer"&gt;prisma generate not working: "No command registered"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prisma 7 throws different errors
&lt;/h2&gt;

&lt;p&gt;Prisma 7.10.0 doesn't ship the placeholder, so the same mistake (no generate) surfaces as a module error instead. Which one depends on how you import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ESM (.mjs):   SyntaxError: Named export 'PrismaClient' not found. The requested module '@prisma/client' is a CommonJS module...
TypeScript:   SyntaxError: The requested module '@prisma/client' does not provide an export named 'PrismaClient'
CommonJS:     Error: Cannot find module '.prisma/client/default'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ESM message even suggests &lt;code&gt;import pkg from '@prisma/client'&lt;/code&gt;. That changes nothing here: the module still has nothing in it until generate runs. The fix is the same &lt;code&gt;prisma generate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once that's done, Prisma 7's next error is usually this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PrismaClientInitializationError: PrismaClient was instantiated without any options. A driver adapter is required to connect to your database.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prisma 7 needs a driver adapter. For Postgres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaPg&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@prisma/adapter-pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaPg&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ With &lt;code&gt;@prisma/adapter-pg&lt;/code&gt; 7.10.0 this ran an &lt;code&gt;upsert&lt;/code&gt; against Postgres 17.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Reinstalling &lt;code&gt;@prisma/client&lt;/code&gt;.&lt;/strong&gt; On Windows, deleting &lt;code&gt;node_modules&lt;/code&gt; and running &lt;code&gt;npm ci&lt;/code&gt; again brought the same error back every time. The install never generated there.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;&lt;code&gt;npm rebuild @prisma/client&lt;/code&gt;.&lt;/strong&gt; It re-ran Prisma's install script, which again finished silently without generating.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;The default import the ESM error suggests.&lt;/strong&gt; In Prisma 7 it just moved the failure, because there was still nothing to import.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How this was tested
&lt;/h2&gt;

&lt;p&gt;One small project (a &lt;code&gt;User&lt;/code&gt; model, the &lt;code&gt;prisma-client-js&lt;/code&gt; generator, a script that does &lt;code&gt;new PrismaClient()&lt;/code&gt;) installed from the same lockfile on Windows 11 with npm 11.9.0, in PowerShell and Git Bash, and on Ubuntu 24.04 in WSL with npm 11.19.0 and npm 12.2.0. Prisma 6.19.3 for the main error, then 7.10.0 for the Prisma 7 messages, run as CommonJS, ESM and TypeScript (tsx). Every fix was checked from a clean &lt;code&gt;node_modules&lt;/code&gt;. I didn't test Docker, Vercel, pnpm or Yarn. A production install without dev dependencies (&lt;code&gt;npm ci --omit=dev&lt;/code&gt;) didn't reproduce the error on Linux, so it isn't listed as a cause.&lt;/p&gt;

&lt;p&gt;The code for every case above is public, so you can run it yourself: &lt;a href="https://github.com/nils44344/nk-repro/tree/main/prisma6-noinit" rel="noopener noreferrer"&gt;&lt;code&gt;prisma6-noinit&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/nils44344/nk-repro/tree/main/prisma-noinit" rel="noopener noreferrer"&gt;&lt;code&gt;prisma-noinit&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/nils44344/nk-repro" rel="noopener noreferrer"&gt;nk-repro&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published, with updates as new versions ship, at &lt;a href="https://nilaykabariya.blog/database/prisma-client-did-not-initialize-yet" rel="noopener noreferrer"&gt;nilaykabariya.blog&lt;/a&gt;. Written with AI assistance; every error and fix was reproduced on a real machine before it was written up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>prisma</category>
      <category>npm</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>Next.js 16.3 vulnerabilities: which version is patched?</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Fri, 09 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/nils44344/nextjs-163-vulnerabilities-which-version-is-patched-2ikm</link>
      <guid>https://dev.to/nils44344/nextjs-163-vulnerabilities-which-version-is-patched-2ikm</guid>
      <description>&lt;p&gt;&lt;strong&gt;Verdict:&lt;/strong&gt; 16.3.0 carries 9 published advisories (3 critical) and 16.3.7 still carries 6, because the September 30 fixes skipped it. The upgrade built cleanly on every version I tried. The traps are in the tools: npm audit fix jumps you a minor version or does nothing at all, and next upgrade crashes on Windows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tested on:&lt;/strong&gt; next: 16.3.0, 16.3.7, 16.3.8, 16.4.0 · npm: 11.9.0 · Node / OS: 24.14.0 / Windows 11 Pro · Advisories: GitHub, as of Oct 8, 2026&lt;/p&gt;

&lt;p&gt;Next.js has published a lot of security fixes since July, and the version numbers don't make it obvious which release actually has them. The worst case is &lt;strong&gt;16.3.7&lt;/strong&gt;: it came out on September 29, the day before a scheduled security release, and it's a bug-fix release only. Next.js's own &lt;a href="https://nextjs.org/blog/upcoming-nextjs-security-release-september-2026" rel="noopener noreferrer"&gt;announcement&lt;/a&gt; says so.&lt;/p&gt;

&lt;p&gt;So I took one small App Router app (an image, a server action, a &lt;code&gt;next/og&lt;/code&gt; route and middleware: the features the advisories touch) and ran &lt;code&gt;npm audit&lt;/code&gt; and &lt;code&gt;next build&lt;/code&gt; on it at each step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What npm audit says at each version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;next&lt;/th&gt;
&lt;th&gt;npm audit for &lt;code&gt;next&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Build&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;16.3.0&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;9 advisories&lt;/strong&gt;: 3 critical, 1 high, 4 moderate, 1 low&lt;/td&gt;
&lt;td&gt;✅ passes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16.3.7&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;6 advisories&lt;/strong&gt;: 1 high, 4 moderate, 1 low&lt;/td&gt;
&lt;td&gt;✅ passes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16.3.8&lt;/td&gt;
&lt;td&gt;not flagged&lt;/td&gt;
&lt;td&gt;✅ passes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16.4.0 (&lt;code&gt;latest&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;not flagged&lt;/td&gt;
&lt;td&gt;✅ passes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To stay on the 16.3 line, install the patched release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i next@16.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That resolved to &lt;strong&gt;16.3.8&lt;/strong&gt;. If you're ready for the newer minor version, &lt;code&gt;npm i next@latest&lt;/code&gt; gives you 16.4.0. Both builds of the test app passed with no new warnings.&lt;/p&gt;

&lt;p&gt;The only warning on every version was one that has nothing to do with security: &lt;code&gt;The "middleware" file convention is deprecated. Please use "proxy" instead.&lt;/code&gt; It's printed on 16.3.0 too, so it isn't something the upgrade added.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which release fixed what
&lt;/h2&gt;

&lt;p&gt;From the &lt;a href="https://github.com/vercel/next.js/security/advisories" rel="noopener noreferrer"&gt;published advisories&lt;/a&gt; for the 16.3 line:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Released&lt;/th&gt;
&lt;th&gt;Patched in&lt;/th&gt;
&lt;th&gt;Fixed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Aug 25&lt;/td&gt;
&lt;td&gt;16.3.3, 15.5.24&lt;/td&gt;
&lt;td&gt;Two &lt;strong&gt;critical&lt;/strong&gt; remote code execution bugs: one in the Image Optimization API with AVIF files, one on Windows-hosted servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sep 22&lt;/td&gt;
&lt;td&gt;16.3.6, 15.5.26&lt;/td&gt;
&lt;td&gt;A &lt;strong&gt;critical&lt;/strong&gt; remote code execution bug in &lt;code&gt;next/og&lt;/code&gt;'s &lt;code&gt;ImageResponse&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sep 30&lt;/td&gt;
&lt;td&gt;16.3.8, 15.5.27&lt;/td&gt;
&lt;td&gt;Seven more: a &lt;strong&gt;high&lt;/strong&gt; SSRF in Image Optimization, five medium (cache poisoning of SSG/ISR pages, &lt;code&gt;use cache&lt;/code&gt; leaks, a metadata image route disclosure) and one low (the dev server's MCP endpoint)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The September 30 announcement also says two more issues, one critical and one high, are "pending upstream coordination and will be addressed in a later Next.js release". So today's patched release won't be the last one you need. &lt;code&gt;npm audit&lt;/code&gt; can only flag what's been published.&lt;/p&gt;

&lt;p&gt;I tested the 16.x line only. The 15.x versions above come from the advisories.&lt;/p&gt;

&lt;h2&gt;
  
  
  npm audit fix doesn't do what you'd expect
&lt;/h2&gt;

&lt;p&gt;The obvious fix behaved differently depending on how &lt;code&gt;next&lt;/code&gt; was written in &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;package.json had&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;npm audit fix&lt;/code&gt; result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;"next": "^16.3.0"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installed &lt;strong&gt;16.4.0&lt;/strong&gt;, a new minor version, not the 16.3 patch release. &lt;code&gt;package.json&lt;/code&gt; unchanged.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;"next": "16.3.0"&lt;/code&gt; (exact)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Did nothing.&lt;/strong&gt; Printed &lt;code&gt;fix available via npm audit fix --force&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;exact, then &lt;code&gt;npm audit fix --force&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Installed 16.4.0 and &lt;strong&gt;rewrote the pin to &lt;code&gt;^16.4.0&lt;/code&gt;&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The exact pin isn't unusual: &lt;code&gt;create-next-app@16.3.0&lt;/code&gt; writes &lt;code&gt;"next": "16.3.0"&lt;/code&gt; exactly. So on a freshly scaffolded project, &lt;code&gt;npm audit fix&lt;/code&gt; leaves every advisory in place, and the forced version quietly removes the pin you had. &lt;code&gt;npm update next&lt;/code&gt; on a caret range also went to 16.4.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  next upgrade crashes on Windows
&lt;/h2&gt;

&lt;p&gt;Next.js has its own upgrade command, and on Windows it failed immediately, from PowerShell and from Git Bash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx next upgrade --revision 16.3.8
Error: spawn npx ENOENT
    at onErrorNT (node:internal/child_process:484:16)
  ...
  spawnargs: [ '--yes', '@next/codemod@canary', 'upgrade', '16.3.8' ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's reported upstream (&lt;a href="https://github.com/vercel/next.js/issues/97980" rel="noopener noreferrer"&gt;vercel/next.js#97980&lt;/a&gt;). The fix (&lt;a href="https://github.com/vercel/next.js/pull/98075" rel="noopener noreferrer"&gt;#98075&lt;/a&gt;) was still unmerged when I tested, and 16.4.0's &lt;code&gt;next upgrade&lt;/code&gt; crashed the same way.&lt;/p&gt;

&lt;p&gt;Run the command &lt;code&gt;next upgrade&lt;/code&gt; tries to launch, directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; @next/codemod@canary upgrade 16.3.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ On Windows this upgraded 16.3.0 to 16.3.8, &lt;strong&gt;kept the exact pin&lt;/strong&gt; (&lt;code&gt;"next": "16.3.8"&lt;/code&gt;), and npm reported &lt;code&gt;found 0 vulnerabilities&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What's new in 16.4.0
&lt;/h2&gt;

&lt;p&gt;16.4.0 came out on October 6. From its &lt;a href="https://github.com/vercel/next.js/releases/tag/v16.4.0" rel="noopener noreferrer"&gt;release notes&lt;/a&gt;: &lt;code&gt;next dev&lt;/code&gt; and &lt;code&gt;next build&lt;/code&gt; now nudge you when your version has known security issues, &lt;code&gt;next upgrade --ai&lt;/code&gt; adds vulnerability coverage, and the Pages Router deprecates React 18. I only built the test app on it (it passed); I didn't test the nudges or the AI upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Upgrading to 16.3.7.&lt;/strong&gt; It looks like the newest 16.3 patch from September, but it's the bug-fix release from the day before the security release. npm audit still flagged 6 advisories.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;&lt;code&gt;npm audit fix&lt;/code&gt; on an exact version.&lt;/strong&gt; It changed nothing and asked for &lt;code&gt;--force&lt;/code&gt;, which then moved to 16.4.0 and dropped the pin.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;&lt;code&gt;next upgrade&lt;/code&gt; on Windows.&lt;/strong&gt; &lt;code&gt;spawn npx ENOENT&lt;/code&gt; on 16.3.0 and on 16.4.0.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How this was tested
&lt;/h2&gt;

&lt;p&gt;One App Router app (TypeScript, &lt;code&gt;next/image&lt;/code&gt;, a server action, a &lt;code&gt;next/og&lt;/code&gt; route, a middleware file) on Windows 11 with Node 24.14.0 and npm 11.9.0. For each of next 16.3.0, 16.3.7, 16.3.8 and 16.4.0: &lt;code&gt;npm audit --json&lt;/code&gt; (counting the advisories listed under &lt;code&gt;next&lt;/code&gt;) and &lt;code&gt;next build&lt;/code&gt; (Turbopack). Then &lt;code&gt;npm audit fix&lt;/code&gt;, &lt;code&gt;npm audit fix --force&lt;/code&gt;, &lt;code&gt;npm update next&lt;/code&gt; and a direct install of the 16.3 line, each starting again from 16.3.0, with both caret and exact versions in &lt;code&gt;package.json&lt;/code&gt;. Severities and counts are npm's on October 8, 2026, and will change as new advisories are published. I didn't test the vulnerabilities themselves, the Pages Router, custom servers or the 15.x line.&lt;/p&gt;

&lt;p&gt;The code for every case above is public, so you can run it yourself: &lt;a href="https://github.com/nils44344/nk-repro/tree/main/next-security" rel="noopener noreferrer"&gt;&lt;code&gt;next-security&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/nils44344/nk-repro" rel="noopener noreferrer"&gt;nk-repro&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published, with updates as new versions ship, at &lt;a href="https://nilaykabariya.blog/tested/nextjs-16-3-vulnerabilities-which-version-is-patched" rel="noopener noreferrer"&gt;nilaykabariya.blog&lt;/a&gt;. Written with AI assistance; every error and fix was reproduced on a real machine before it was written up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>security</category>
      <category>npm</category>
      <category>webdev</category>
    </item>
    <item>
      <title>prisma generate not working: "No command registered" (Prisma 8)</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Wed, 07 Oct 2026 17:17:26 +0000</pubDate>
      <link>https://dev.to/nils44344/prisma-generate-not-working-no-command-registered-prisma-8-7g1</link>
      <guid>https://dev.to/nils44344/prisma-generate-not-working-no-command-registered-prisma-8-7g1</guid>
      <description>&lt;p&gt;&lt;strong&gt;The error:&lt;/strong&gt;&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="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"envelope"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"commandId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"CLI.UNKNOWN_COMMAND"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"No command registered for `generate`"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"nextActions"&lt;/span&gt;&lt;span class="p"&gt;:[{&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"run-command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"List every command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"prisma --help"&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;strong&gt;Tested on:&lt;/strong&gt; prisma (latest tag): 8.0.0-rc.20 · prisma / @prisma/client: 7.10.0 · Postgres: 17 (Docker) · Node / OS: 24.14.0 / Windows 11 Pro&lt;/p&gt;

&lt;p&gt;Nothing is wrong with your schema. On npm, &lt;code&gt;prisma@latest&lt;/code&gt; now points to &lt;strong&gt;8.0.0-rc.20&lt;/strong&gt;, a release candidate of Prisma ORM 8. It's a new command-line tool that doesn't read &lt;code&gt;schema.prisma&lt;/code&gt; and has no &lt;code&gt;generate&lt;/code&gt;, &lt;code&gt;migrate dev&lt;/code&gt; or &lt;code&gt;db push&lt;/code&gt;. Meanwhile &lt;code&gt;@prisma/client@latest&lt;/code&gt; is still &lt;strong&gt;7.10.0&lt;/strong&gt;. So a fresh &lt;code&gt;npm i prisma @prisma/client&lt;/code&gt; today gives you two different major versions, and every Prisma 7 tutorial breaks at the first command.&lt;/p&gt;

&lt;p&gt;I checked it on October 7, 2026 with a fresh install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm view prisma dist-tags.latest
8.0.0-rc.20
$ npm view @prisma/client dist-tags.latest
7.10.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are the Prisma 7 commands I ran against it, each in the CLI's new JSON output:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You ran&lt;/th&gt;
&lt;th&gt;Prisma 8 RC answered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx prisma generate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;No command registered for `generate`&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx prisma migrate dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;No command registered for `migrate`, did you mean `migration`?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx prisma db push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;No command registered for `push`, did you mean `update`?&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx prisma validate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;No command registered for `validate`&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Check which CLI you have
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npx prisma --version&lt;/code&gt; tells you straight away. Prisma 7 prints a table (&lt;code&gt;prisma : 7.10.0&lt;/code&gt;, &lt;code&gt;@prisma/client : 7.10.0&lt;/code&gt; and so on). The 8 release candidate prints one line of JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"kind":"result","envelope":{"ok":true,"commandId":"version","result":{"version":"8.0.0-rc.20"},...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see JSON, you're on Prisma 8.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix for an existing Prisma 7 app
&lt;/h2&gt;

&lt;p&gt;Prisma 8 isn't final yet: Prisma's own &lt;a href="https://www.prisma.io/docs/orm/release-status" rel="noopener noreferrer"&gt;release status page&lt;/a&gt; calls it a release candidate, with general availability expected in October 2026. The same page says Prisma 7 keeps getting bug fixes and security updates for 18 months after that. If you have a working Prisma 7 app, the move is a rewrite (no &lt;code&gt;@prisma/client&lt;/code&gt;, a new schema format, new commands), not a version bump.&lt;/p&gt;

&lt;p&gt;Pin the CLI to the same major version as your client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; prisma@7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;npm saves that as &lt;code&gt;^7.10.0&lt;/code&gt;, which stays on 7.x. Then the usual commands work again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx prisma generate
✔ Generated Prisma Client (7.10.0) to .\src\generated\prisma in 117ms
$ npx prisma migrate dev --name init
Datasource "db": PostgreSQL database "postgres", schema "public" at "localhost:55432"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ After the pin, &lt;code&gt;generate&lt;/code&gt;, &lt;code&gt;migrate dev&lt;/code&gt; and a real query (an &lt;code&gt;upsert&lt;/code&gt; through &lt;code&gt;@prisma/adapter-pg&lt;/code&gt;) all worked against Postgres 17.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Docker and CI: &lt;code&gt;npx prisma&lt;/code&gt; without a version
&lt;/h2&gt;

&lt;p&gt;This is where most of the breakage is. A Dockerfile or CI step that runs &lt;code&gt;npx prisma generate&lt;/code&gt; in a place where Prisma isn't installed (a slim production image, a separate migration job) makes npx download whatever &lt;code&gt;latest&lt;/code&gt; is. I ran it in an empty folder with only &lt;code&gt;prisma/schema.prisma&lt;/code&gt; in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx -y prisma generate
{"kind":"result","envelope":{"ok":false,...,"summary":"No command registered for `generate`",...}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Public repos have been filing the same failure since early September: Docker images, Railway deploys, a self-hosted release script, and Prisma's own Claude Code plugin (&lt;em&gt;"unpinned &lt;code&gt;npx -y prisma mcp&lt;/code&gt; now resolves to 8.0.0"&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Put the major version in the command itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;-y&lt;/span&gt; prisma@7 generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ In the same empty folder this printed &lt;code&gt;✔ Generated Prisma Client (7.10.0)&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Better still, keep &lt;code&gt;prisma&lt;/code&gt; in &lt;code&gt;devDependencies&lt;/code&gt; with the &lt;code&gt;^7&lt;/code&gt; range and install from your lockfile (&lt;code&gt;npm ci&lt;/code&gt;) before running it. Then npx uses the local copy instead of downloading &lt;code&gt;latest&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you already ran &lt;code&gt;prisma init&lt;/code&gt; on the new CLI
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npx prisma init&lt;/code&gt; with 8.0.0-rc.20 doesn't create &lt;code&gt;prisma/schema.prisma&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt;. In my test it created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;prisma.config.ts&lt;/code&gt; that imports &lt;code&gt;definePrismaConfig&lt;/code&gt; and only lists AI agents,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.claude&lt;/code&gt;, &lt;code&gt;.cursor&lt;/code&gt;, &lt;code&gt;.agents&lt;/code&gt; and &lt;code&gt;.devin&lt;/code&gt; folders with Prisma "skills" in them,&lt;/li&gt;
&lt;li&gt;a &lt;code&gt;postinstall&lt;/code&gt; script in &lt;code&gt;package.json&lt;/code&gt;: &lt;code&gt;prisma skills sync || exit 0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That config file breaks the old CLI even after the fix above. With it in place, Prisma 7.10.0 refused to start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed to load config file "D:\nk-repro\prisma-init" as a TypeScript/JavaScript module.
Error: TypeError: (0 , _config.definePrismaConfig) is not a function
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;prisma.config.ts&lt;/code&gt; with a Prisma 7 config (Prisma 7 reads the connection URL from here, not from the schema):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prisma/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prisma/schema.prisma&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;migrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;prisma/migrations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;datasource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then delete the &lt;code&gt;postinstall&lt;/code&gt; line, and the four agent folders if you don't want them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ With this config, Prisma 7.10.0 printed &lt;code&gt;Loaded Prisma config from prisma.config.ts&lt;/code&gt; and ran &lt;code&gt;migrate dev&lt;/code&gt; normally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Watch out for the update banner
&lt;/h2&gt;

&lt;p&gt;Once the fix above is in, the Prisma 7 CLI shows an "update available" box at the end of some commands. Mine ended with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;│  Run the following to update                            │
│    npm i --save-dev prisma@latest                       │
│    npm i @prisma/client@latest                          │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that puts you straight back on the release candidate. Ignore it until you decide to move to Prisma 8 on purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Prisma 8 instead
&lt;/h2&gt;

&lt;p&gt;If you're starting a new project and want Prisma 8, these are the replacements from &lt;a href="https://www.prisma.io/docs/orm/coming-from-prisma-orm-7" rel="noopener noreferrer"&gt;Prisma's migration page&lt;/a&gt;. I confirmed that the old names fail on 8.0.0-rc.20, but &lt;strong&gt;I haven't built a project on Prisma 8&lt;/strong&gt;, so treat this as a map, not tested steps:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prisma 7&lt;/th&gt;
&lt;th&gt;Prisma 8 (per Prisma's docs)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prisma init&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prisma orm init --write-env&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prisma generate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;prisma contract emit&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prisma migrate dev&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;prisma db update&lt;/code&gt;, or &lt;code&gt;prisma migration plan&lt;/code&gt; then &lt;code&gt;prisma db migrate&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prisma db push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;prisma db init&lt;/code&gt; (empty database) or &lt;code&gt;prisma db update&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@prisma/client&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;one package per database, e.g. &lt;code&gt;@prisma/orm-postgres&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What didn't work
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Reinstalling &lt;code&gt;@prisma/client&lt;/code&gt;.&lt;/strong&gt; It was never the problem: it installs 7.10.0, the version that works. The CLI is the part that moved.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Deleting &lt;code&gt;node_modules&lt;/code&gt; and the lockfile.&lt;/strong&gt; That makes it worse. A clean &lt;code&gt;npm i prisma&lt;/code&gt; resolves &lt;code&gt;latest&lt;/code&gt; again and brings the release candidate straight back, unless the version is pinned.&lt;/p&gt;

&lt;p&gt;❌ &lt;strong&gt;Pinning the CLI but keeping the generated &lt;code&gt;prisma.config.ts&lt;/code&gt;.&lt;/strong&gt; Prisma 7 crashed on it with the &lt;code&gt;definePrismaConfig is not a function&lt;/code&gt; error above.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  How this was tested
&lt;/h2&gt;

&lt;p&gt;A fresh npm project on Windows 11 with Node 24.14.0, created with &lt;code&gt;npm i -D prisma&lt;/code&gt; and &lt;code&gt;npm i @prisma/client&lt;/code&gt; on October 7, 2026, which installed prisma 8.0.0-rc.20 and @prisma/client 7.10.0. Every Prisma 7 command above was run against that CLI, then again after the fix, against Postgres 17 in Docker. The CI case was a folder containing only &lt;code&gt;prisma/schema.prisma&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt;, with nothing installed, running &lt;code&gt;npx -y prisma generate&lt;/code&gt; and then the version-pinned command from the Docker section. npm dist-tags can change at any time: when Prisma 8 reaches its final release, &lt;code&gt;latest&lt;/code&gt; will point to it for good, and the fix above stays the way to keep a Prisma 7 app running.&lt;/p&gt;

&lt;p&gt;The code for every case above is public, so you can run it yourself: &lt;a href="https://github.com/nils44344/nk-repro/tree/main/prisma-init" rel="noopener noreferrer"&gt;&lt;code&gt;prisma-init&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/nils44344/nk-repro/tree/main/prisma-ci" rel="noopener noreferrer"&gt;&lt;code&gt;prisma-ci&lt;/code&gt;&lt;/a&gt; in &lt;a href="https://github.com/nils44344/nk-repro" rel="noopener noreferrer"&gt;nk-repro&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published, with updates as new versions ship, at &lt;a href="https://nilaykabariya.blog/database/prisma-generate-no-command-registered-prisma-8" rel="noopener noreferrer"&gt;nilaykabariya.blog&lt;/a&gt;. Written with AI assistance; every error and fix was reproduced on a real machine before it was written up.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>prisma</category>
      <category>npm</category>
      <category>node</category>
      <category>database</category>
    </item>
    <item>
      <title>How I Built a Free, Self-Hosted Pipeline That Auto-Generates Faceless YouTube Shorts</title>
      <dc:creator>nils44344</dc:creator>
      <pubDate>Sat, 23 May 2026 13:35:32 +0000</pubDate>
      <link>https://dev.to/nils44344/how-i-built-a-free-self-hosted-pipeline-that-auto-generates-faceless-youtube-shorts-3je4</link>
      <guid>https://dev.to/nils44344/how-i-built-a-free-self-hosted-pipeline-that-auto-generates-faceless-youtube-shorts-3je4</guid>
      <description>&lt;p&gt;Every "AI YouTube" tutorial ends the same way: sign up for ChatGPT Plus, then ElevenLabs, then Pictory, then n8n Cloud. Add it up and you're paying &lt;strong&gt;$75–100/month&lt;/strong&gt; before you've made a single video — let alone a single dollar.&lt;/p&gt;

&lt;p&gt;I didn't want a subscription stack. I wanted something that ran on my own machine, used free tiers and local models, and that I actually owned. So I built it, and I just open-sourced it under MIT.&lt;/p&gt;

&lt;p&gt;It's called &lt;strong&gt;FreeFaceless&lt;/strong&gt;, and it takes one command to go from nothing to an uploaded Short:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script → voiceover → captions → b-roll → assembled video → YouTube upload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repo: &lt;strong&gt;&lt;a href="https://github.com/nils44344/FreeFaceless" rel="noopener noreferrer"&gt;https://github.com/nils44344/FreeFaceless&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's how each stage works — and the one bug that cost me an evening.&lt;/p&gt;

&lt;h2&gt;
  
  
  The orchestration
&lt;/h2&gt;

&lt;p&gt;The whole thing is a linear pipeline. Here's the heart of it (trimmed):&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publish_at&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upload_to_youtube&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                          &lt;span class="c1"&gt;# 1. Groq writes the script
&lt;/span&gt;    &lt;span class="n"&gt;voice_mp3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;full_text&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="c1"&gt;# 2. edge-tts voiceover
&lt;/span&gt;    &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transcribe_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;voice_mp3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# 3. local Whisper timing
&lt;/span&gt;    &lt;span class="n"&gt;scenes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;visuals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch_for_scenes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scenes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="c1"&gt;# 4. Pexels b-roll
&lt;/span&gt;    &lt;span class="n"&gt;ass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;captions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_ass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;              &lt;span class="c1"&gt;# 5. caption file
&lt;/span&gt;    &lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assemble&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scenes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;voice_mp3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 6. ffmpeg
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;upload_to_youtube&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_video&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 7. YouTube Data API
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every stage is its own module, and everything is driven by a single &lt;code&gt;config.yaml&lt;/code&gt; — so changing the niche, voice, or caption style is an edit, not a code change.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Script generation — Groq (free tier)
&lt;/h2&gt;

&lt;p&gt;Groq's free tier serves Llama 3.3 70B fast, and it's OpenAI-compatible, so the official &lt;code&gt;openai&lt;/code&gt; SDK works by just pointing the base URL at Groq:&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;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;GROQ_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.groq.com/openai/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama-3.3-70b-versatile&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;response_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&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="s"&gt;json_object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;  &lt;span class="c1"&gt;# forces clean JSON
&lt;/span&gt;    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&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="s"&gt;system&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="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_PROMPT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;...],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The prompt asks for a hook + 4–6 facts + a CTA, returned as JSON with per-scene &lt;code&gt;visual_query&lt;/code&gt; strings I can feed straight to stock search. JSON mode means no fragile regex parsing.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Voiceover — edge-tts (free, no key)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;edge-tts&lt;/code&gt; exposes Microsoft's neural voices for free, no API key:&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;edge_tts&lt;/span&gt;
&lt;span class="n"&gt;communicate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;edge_tts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Communicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US-ChristopherNeural&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-12%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;communicate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;voice.mp3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quality is genuinely good enough for faceless content, and there are dozens of voices/accents to match the niche.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Word-level captions — faster-whisper (local)
&lt;/h2&gt;

&lt;p&gt;This is the part most paid tools charge per-minute for. &lt;code&gt;faster-whisper&lt;/code&gt; runs locally on CPU and gives &lt;strong&gt;word-level timestamps&lt;/strong&gt;, which I turn into karaoke-style captions:&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;from&lt;/span&gt; &lt;span class="n"&gt;faster_whisper&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WhisperModel&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WhisperModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compute_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;int8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;segments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transcribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;voice.mp3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;word_timestamps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I write an ASS subtitle file, 3 words at a time, in a big bold style — the look every Shorts channel uses. (FreeFaceless ships the open-licensed &lt;strong&gt;Anton&lt;/strong&gt; font so it works out of the box.)&lt;/p&gt;

&lt;h2&gt;
  
  
  4. B-roll — Pexels (free API)
&lt;/h2&gt;

&lt;p&gt;Each scene's &lt;code&gt;visual_query&lt;/code&gt; becomes a Pexels Videos search, pulling vertical clips. Free API, generous limits.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Assembly — ffmpeg
&lt;/h2&gt;

&lt;p&gt;ffmpeg crops every clip to 1080×1920, concatenates them to match the voiceover length, overlays the audio, and burns in the captions:&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-vf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;subtitles=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ass_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;:fontsdir=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;fonts_dir&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Upload — YouTube Data API
&lt;/h2&gt;

&lt;p&gt;OAuth desktop flow, token cached after the first browser login, then every future run refreshes silently. Supports immediate or scheduled publishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that cost me an evening: SSL on Windows
&lt;/h2&gt;

&lt;p&gt;On my machine, every HTTPS call died with &lt;code&gt;CERTIFICATE_VERIFY_FAILED&lt;/code&gt;. The culprit: antivirus doing TLS interception with a custom root cert that Python's bundled &lt;code&gt;certifi&lt;/code&gt; doesn't know about. The fix is one import, before any network client is built:&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;truststore&lt;/span&gt;
&lt;span class="n"&gt;truststore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inject_into_ssl&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# use the OS cert store instead of certifi
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you build anything network-heavy on Windows, keep this in your back pocket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free tiers are rate-limited.&lt;/strong&gt; This is built for one channel on a normal schedule, not bulk farms. Push it hard and you'll hit limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows-first.&lt;/strong&gt; The Python core runs anywhere; the helper scripts are PowerShell. Cross-platform PRs very welcome.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's a production tool, not a money machine.&lt;/strong&gt; It automates &lt;em&gt;making&lt;/em&gt; videos. Views and revenue depend on your content and the algorithm — no tool changes that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it / contribute
&lt;/h2&gt;

&lt;p&gt;The repo has a full setup guide (including the Google OAuth walkthrough, which is the only fiddly part):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/nils44344/FreeFaceless" rel="noopener noreferrer"&gt;https://github.com/nils44344/FreeFaceless&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it's useful, a star helps other people find it — and I'd genuinely love feedback, especially on making the setup smoother for non-developers and getting it running on macOS/Linux.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>ai</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
