[{"data":1,"prerenderedAt":467},["ShallowReactive",2],{"blog-en-new-row-violates-row-level-security-policy":3},{"id":4,"title":5,"body":6,"category":425,"cover":426,"coverAlt":427,"description":428,"draft":429,"extension":430,"faq":431,"image":447,"keywords":448,"meta":455,"navigation":456,"ogTitle":457,"path":458,"published":459,"seo":460,"stem":461,"tldr":462,"updated":459,"__hash__":466},"blog_en\u002Fblog\u002Fnew-row-violates-row-level-security-policy.md","New row violates row-level security policy in Supabase. Now what?",{"type":7,"value":8,"toc":414},"minimark",[9,13,24,32,37,40,47,50,63,66,70,73,76,79,83,86,96,99,182,188,195,223,231,235,238,241,247,254,262,266,269,275,285,298,302,305,310,313,322,337,351,359,363,401],[10,11,12],"p",{},"Something told you to turn Row Level Security on. The advisor inside your\nSupabase dashboard, a scan result, a checklist, somebody in a Discord. You\nturned it on. Now your app cannot save anything at all. Every attempt comes back\nsaying that a new row violates row-level security policy:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","new row violates row-level security policy for table \"profiles\"\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,31],{},"Here is the part that guide after guide gets wrong: ",[28,29,30],"strong",{},"the answer that clears\nthis error in ten seconds also undoes the thing you just switched on."," It is\nthe first result you will find, it works immediately, and it leaves the table\nexactly where it was before anybody told you to fix it.",[33,34,36],"h2",{"id":35},"what-new-row-violates-row-level-security-policy-means","What \"new row violates row-level security policy\" means",[10,38,39],{},"Your database was asked to store a row, checked the rules on that table, found\nnone of them allowing that particular row in, and refused.",[10,41,42,43,46],{},"That is the entire message. Nothing is corrupt and nothing was lost: the row was\nnever saved, and the rest of the table sits exactly as it did a minute ago. The\nwording comes from Postgres, the database engine Supabase runs on, which is why\nit reads like machinery rather than like anything your builder wrote. Supabase\npasses it through with its number attached, ",[21,44,45],{},"42501",".",[10,48,49],{},"What the message will not tell you is which rule was missing. One sentence\ncovers three different situations, and telling you which one you hit would\ndescribe your rules to whoever triggered the error:",[51,52,53,57,60],"ul",{},[54,55,56],"li",{},"The table has Row Level Security switched on and no rules written at all.",[54,58,59],{},"It has a rule, and that rule is only about reading.",[54,61,62],{},"It has a rule about writing, and the row you sent does not satisfy it.",[10,64,65],{},"All three print that identical line. The second is the common one in a newly\nlaunched app, where a rule was added to get the screens working again and saving\nnever came up.",[33,67,69],{"id":68},"why-it-appeared-the-moment-you-turned-row-level-security-on","Why it appeared the moment you turned Row Level Security on",[10,71,72],{},"Because switching it on refuses everything until a rule says otherwise, and your\nown app is part of everything.",[10,74,75],{},"This is the sequence almost everybody goes through. You turn the setting on. The\nscreens go blank, because with no rules written the database now refuses to hand\nover rows to anyone, your app included. You add a rule so the lists come back,\nusually the first one that a search result or your builder suggests. The screens\nfill up. And the first time somebody presses save, this error appears, from a\ntable you thought you had already fixed.",[10,77,78],{},"Nothing went wrong in that sequence. The rule you added was about reading, and\nsaving is a different question that nobody had asked yet.",[33,80,82],{"id":81},"a-rule-has-two-halves-and-only-one-of-them-is-about-reading","A rule has two halves, and only one of them is about reading",[10,84,85],{},"A policy is a condition, and where the database applies that condition depends\non what you asked it to do.",[10,87,88,91,92,95],{},[21,89,90],{},"USING"," is applied to rows already sitting in the table. It decides which ones\nyou are allowed to see, change or remove. ",[21,93,94],{},"WITH CHECK"," is applied to the row you\nare trying to create, before it exists anywhere. It decides whether that row is\nallowed to come into being at all.",[10,97,98],{},"That second one is the part that surprises people, because it is a rule about\nsomething that is not there yet.",[100,101,102,118],"table",{},[103,104,105],"thead",{},[106,107,108,112,115],"tr",{},[109,110,111],"th",{},"Your app asks to",[109,113,114],{},"Checked against rows already in the table",[109,116,117],{},"Checked against the row being written",[119,120,121,138,152,168],"tbody",{},[106,122,123,131,135],{},[124,125,126,127,130],"td",{},"read (",[21,128,129],{},"select",")",[124,132,133],{},[21,134,90],{},[124,136,137],{},"nothing to check",[106,139,140,146,148],{},[124,141,142,143,130],{},"save a new row (",[21,144,145],{},"insert",[124,147,137],{},[124,149,150],{},[21,151,94],{},[106,153,154,160,164],{},[124,155,156,157,130],{},"change a row (",[21,158,159],{},"update",[124,161,162],{},[21,163,90],{},[124,165,166],{},[21,167,94],{},[106,169,170,176,180],{},[124,171,172,173,130],{},"remove a row (",[21,174,175],{},"delete",[124,177,178],{},[21,179,90],{},[124,181,137],{},[183,184],"diagram",{"alt":185,"caption":186,"src":187},"One rule drawn twice. On the left it points at a stack of rows already inside a table and two of them come back. On the right the same rule points at a single row drawn in outline, waiting outside the table, and it is turned away.","The same condition, aimed at two different things. On a read it is asked about rows that exist; on a save it is asked about a row that does not exist yet.","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fusing-and-with-check-1600x780.png",[10,189,190,191,194],{},"A policy written ",[21,192,193],{},"FOR SELECT"," only ever carries the first half, because there is\nno new row to check when somebody is reading. So it can never permit a save,\nhowever permissive it looks. That is the whole of the mismatch, and it explains\nwhy your reads recovered and your writes did not.",[196,197,199],"callout",{"type":198},"warn",[10,200,201,207,208,210,211,213,214,216,217,219,220,222],{},[28,202,190,203,206],{},[21,204,205],{},"FOR ALL"," behaves differently, and quietly."," If you write one\nwith a ",[21,209,90],{}," condition and no ",[21,212,94],{},", Postgres applies that same ",[21,215,90],{},"\ncondition to new rows as well. So a ",[21,218,205],{}," rule saying rows belong to their\nowner refuses a save where the owner does not match, without any ",[21,221,94],{},"\nappearing anywhere in what you wrote. Useful when you meant it. Confusing when\nyou are reading a rule somebody else pasted in.",[10,224,225,226,46],{},"If you would rather see this from the outside, our free scan asks your live\ndatabase what a stranger can already read from it, without an account and in\nabout 20 seconds: ",[227,228,230],"a",{"href":229},"\u002F#scan","scan your app",[33,232,234],{"id":233},"the-fix-that-makes-the-error-stop-and-what-it-costs","The fix that makes the error stop, and what it costs",[10,236,237],{},"The fastest way to clear this is a rule that allows every write from anyone,\nwhich is why it is the top answer nearly everywhere you look.",[10,239,240],{},"It usually arrives looking like this:",[14,242,245],{"className":243,"code":244,"language":19},[17],"CREATE POLICY \"Enable insert for all users\"\n  ON public.profiles\n  FOR INSERT\n  WITH CHECK (true);\n",[21,246,244],{"__ignoreMap":23},[10,248,249,250,253],{},"Every row satisfies ",[21,251,252],{},"true",", so every save is allowed, from your app and from\nanybody else holding the key that ships inside it. Turning Row Level Security\nback off does the same job more thoroughly.",[10,255,256,257,261],{},"Both of them work. Both of them leave you where you were before the advisor\nflagged the table, and neither shows any sign afterwards that something is open,\nbecause your app behaves identically in all four combinations of setting and\nrule. A table can be switched on, carry a valid policy, show a green badge in\nyour dashboard and still hand its rows to a stranger. That is\n",[227,258,260],{"href":259},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","the other half of this problem",",\nand it is worth reading before you paste anything.",[33,263,265],{"id":264},"the-rule-that-lets-your-app-save-and-only-your-app","The rule that lets your app save, and only your app",[10,267,268],{},"Name the person the row belongs to, and compare it to whoever is asking:",[14,270,273],{"className":271,"code":272,"language":19},[17],"CREATE POLICY \"Users insert their own rows\"\n  ON public.profiles\n  FOR INSERT\n  TO authenticated\n  WITH CHECK (auth.uid() = user_id);\n",[21,274,272],{"__ignoreMap":23},[10,276,277,280,281,284],{},[21,278,279],{},"auth.uid()"," is whoever is signed in and making the request. ",[21,282,283],{},"user_id"," is the\ncolumn on the row saying who it belongs to. When those two match, the row is\nsaved. When they do not, or when nobody is signed in, it is refused, and the\nperson refused sees the same message you have been looking at.",[10,286,287,288,291,292,294,295,297],{},"Two details in that rule earn their place. ",[21,289,290],{},"TO authenticated"," means the rule\napplies to signed-in visitors; leave it out and the policy applies to everybody,\nstrangers included. And your app has to actually send the ",[21,293,283],{}," column,\nbecause a rule comparing ",[21,296,279],{}," to a value that arrived empty can never\nmatch.",[33,299,301],{"id":300},"when-the-rule-is-right-and-the-error-still-appears","When the rule is right and the error still appears",[10,303,304],{},"Look at who was signed in before you look at the policy again.",[183,306],{"alt":307,"caption":308,"src":309},"Three tables side by side. In each one a row drawn in outline arrives from above and its arrow stops short of the rule below it. The first table's rule is an empty dashed outline, the second's is marked select, the third's is marked insert. One strip runs under all three carrying the code 42501.","Three different causes reaching you as one sentence. The code 42501 is the same in all three, which is why the message on its own cannot tell you which one you are in.","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fthree-causes-one-message-1600x700.png",[10,311,312],{},"Three explanations cover most of what is left:",[10,314,315,318,319,321],{},[28,316,317],{},"Nobody is signed in."," ",[21,320,279],{}," comes back empty for a visitor who has not\nlogged in, so a rule comparing it to an owner column cannot match. This is\nnormal on a signup form, a waiting list or a contact form, and those need their\nown rule describing what a stranger is allowed to add.",[10,323,324,327,328,330,331,333,334,336],{},[28,325,326],{},"The owner column never arrives."," Your rule compares ",[21,329,279],{}," to\n",[21,332,283],{},", and your app sends everything except ",[21,335,283],{},". The comparison runs\nagainst a blank and fails every time.",[10,338,339,342,343,346,347,350],{},[28,340,341],{},"The write is going to Storage."," File uploads land in Supabase Storage, which\nkeeps its own policies on ",[21,344,345],{},"storage.objects"," rather than on your table. A rule\nwritten on ",[21,348,349],{},"profiles"," has nothing to say about a file.",[10,352,353,354,358],{},"There is a fourth explanation, and it is the one worth ruling out early: if\nsaving works from your builder's preview and fails from your live site, the two\nare not using the same key. A secret key ignores every rule you have written,\nwhich is what it is for, so an app that only saves while a secret key is in play\nis an app whose rules have never actually been tested.\n",[227,355,357],{"href":356},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Which API keys are safe in your frontend","\ncovers how to tell one from the other.",[33,360,362],{"id":361},"what-to-do-today","What to do today",[364,365,366],"key-takeaways",{},[51,367,368,371,377,391,394],{},[54,369,370],{},"Read the error as a refusal rather than a fault. The write did not happen, the table is unchanged, and nothing needs recovering.",[54,372,373,374,376],{},"Check whether the table has any policy about writing at all. A rule written ",[21,375,193],{}," fixed your screens and said nothing about saving.",[54,378,379,380,383,384,386,387,390],{},"Add a policy ",[21,381,382],{},"FOR INSERT"," whose ",[21,385,94],{}," condition names the row's owner, and add the ",[21,388,389],{},"TO"," clause you meant.",[54,392,393],{},"Confirm your app actually sends the owner column, then try the save again while signed in.",[54,395,396,397,400],{},"Leave ",[21,398,399],{},"WITH CHECK (true)"," for tables you would be comfortable publishing on a public page. For anything with a person in it, spend the extra two minutes.",[10,402,403,404,408,409,413],{},"Start with the table the error came from, and check every other table you turned\nthe setting on for the same afternoon. The\n",[227,405,407],{"href":406},"\u002Fchecklist","10-minute security checklist"," covers what else tends to be left\nopen in a newly launched app, and the\n",[227,410,412],{"href":411},"\u002Fis-your-supabase-app-safe","Supabase safety guide"," goes through the rest of\nwhat a stranger can reach.",{"title":23,"searchDepth":415,"depth":415,"links":416},3,[417,419,420,421,422,423,424],{"id":35,"depth":418,"text":36},2,{"id":68,"depth":418,"text":69},{"id":81,"depth":418,"text":82},{"id":233,"depth":418,"text":234},{"id":264,"depth":418,"text":265},{"id":300,"depth":418,"text":301},{"id":361,"depth":418,"text":362},"Security basics","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcover-1200x630.png","A stack of rows inside a table, with one more row waiting outside it, drawn in outline because it has not been let in yet.","\"New row violates row-level security policy\" means Supabase refused a write. The fix that clears it in ten seconds also reopens the table to everyone.",false,"md",[432,435,438,441,444],{"q":433,"a":434},"What does \"new row violates row-level security policy\" mean?","It means your database was asked to store a row, checked the rules on that table, and found that none of them allowed that row in. So it refused the write. Nothing was lost and nothing is broken, because the row was never saved and the rest of the table is untouched. The message comes from Postgres, the database engine Supabase runs on, and it carries the code 42501. What it deliberately does not tell you is which rule was missing, because saying so would describe your rules to whoever triggered the error.",{"q":436,"a":437},"I added a policy and reads work, so why does saving still fail?","Because a policy about reading has nothing to say about writing. A rule carries a USING half, which the database applies to rows already in the table, and a WITH CHECK half, which it applies to the row you are trying to create. A policy written FOR SELECT only ever has the first one, since there is no new row to check when you are reading. Your screens fill up again and the first save still fails. Add a second policy FOR INSERT with a WITH CHECK condition.",{"q":439,"a":440},"Should I just turn Row Level Security off to make this go away?","That does clear the error, and it leaves every row in that table readable by anyone who has the key that ships inside your app. Your app works either way, so nothing afterwards tells you which of the two you chose. If the table holds people, orders or messages, the two minutes it takes to write a real rule is the difference between a private table and a public one.",{"q":442,"a":443},"My policy looks right and inserts still fail. What else could it be?","Three things account for most of these. Nobody is signed in, so auth.uid() is empty and a rule comparing it to an owner column can never match, which is normal on a signup or contact form. Or your app never sends the owner column at all, so the rule compares against a value that arrived blank. Or the write is going into Supabase Storage rather than a table, and Storage keeps its own policies on storage.objects.",{"q":445,"a":446},"Does this error mean somebody tried to attack my app?","Almost never. In a newly launched app it is nearly always your own app being refused, because Row Level Security was switched on before a rule existed that covered your own writes. It is worth reading rather than dismissing, though: the same message appears when a request that should not be writing to that table is refused, and you cannot tell the two apart from the message alone.","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",[449,450,451,452,453,454],"new row violates row-level security policy","supabase rls insert policy","rls blocking insert","supabase with check policy","enable row level security supabase","row level security policy error",{},true,"New row violates row-level security policy","\u002Fblog\u002Fnew-row-violates-row-level-security-policy","2026-08-27",{"title":5,"description":428},"blog\u002Fnew-row-violates-row-level-security-policy",[463,464,465],"New row violates row-level security policy means your database checked the rules on that table and found none of them allowing the row you were saving. It refused the write and kept the table as it was.","One message covers three different situations: no rules at all, a rule that is only about reading, or a rule about writing that your row does not satisfy.","The answer at the top of every search result makes the error stop by allowing every write from anyone. The rule you actually want takes about two more minutes.","kr4S6Z6kaKo80ouV8_aINxkg99h980smoypR5Ono1KY",1787826048204]