[{"data":1,"prerenderedAt":467},["ShallowReactive",2],{"blog-es-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_es\u002Fblog\u002Fnew-row-violates-row-level-security-policy.md","New row violates row-level security policy en Supabase. ¿Y ahora?",{"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",{},"Algo te dijo que activaras Row Level Security. El advisor dentro de tu panel de\nSupabase, el resultado de un escaneo, una checklist, alguien en un Discord. Lo\nactivaste. Ahora tu app no puede guardar absolutamente nada. Cada intento vuelve\ndiciendo que 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],{},"Aquí está la parte que guía tras guía cuenta mal: ",[28,29,30],"strong",{},"la respuesta que quita este\nerror en diez segundos deshace justo lo que acabas de activar."," Es el primer\nresultado que vas a encontrar, funciona al instante, y deja la tabla exactamente\ndonde estaba antes de que nadie te dijera que la arreglaras.",[33,34,36],"h2",{"id":35},"qué-significa-new-row-violates-row-level-security-policy","Qué significa \"new row violates row-level security policy\"",[10,38,39],{},"Se le pidió a tu base de datos guardar una fila, comprobó las reglas de esa\ntabla, no encontró ninguna que dejara entrar esa fila en concreto, y la rechazó.",[10,41,42,43,46],{},"Ese es el mensaje entero. Nada está corrupto y nada se perdió: la fila nunca\nllegó a guardarse, y el resto de la tabla está igual que hace un minuto. La\nredacción viene de Postgres, el motor de base de datos sobre el que corre\nSupabase, y por eso suena a maquinaria en vez de a algo escrito por tu builder.\nSupabase lo pasa tal cual, con su número al lado, ",[21,44,45],{},"42501",".",[10,48,49],{},"Lo que el mensaje no te va a decir es qué regla faltaba. Una sola frase cubre\ntres situaciones distintas, y decirte en cuál estás describiría tus reglas a\nquien haya provocado el error:",[51,52,53,57,60],"ul",{},[54,55,56],"li",{},"La tabla tiene Row Level Security activado y ninguna regla escrita.",[54,58,59],{},"Tiene una regla, y esa regla solo habla de lectura.",[54,61,62],{},"Tiene una regla de escritura, y la fila que enviaste no la cumple.",[10,64,65],{},"Las tres imprimen esa misma línea. La segunda es la habitual en una app recién\nlanzada, donde se añadió una regla para que las pantallas volvieran a funcionar\ny de guardar no se habló nunca.",[33,67,69],{"id":68},"por-qué-apareció-justo-al-activar-row-level-security","Por qué apareció justo al activar Row Level Security",[10,71,72],{},"Porque activarlo rechaza todo hasta que una regla diga otra cosa, y tu propia\napp forma parte de todo.",[10,74,75],{},"Esta es la secuencia por la que pasa casi todo el mundo. Activas el ajuste. Las\npantallas se quedan en blanco, porque sin reglas escritas la base de datos ya no\nentrega filas a nadie, tu app incluida. Añades una regla para que las listas\nvuelvan, normalmente la primera que sugiere un resultado de búsqueda o tu\nbuilder. Las pantallas se llenan. Y la primera vez que alguien pulsa guardar\naparece este error, en una tabla que dabas por arreglada.",[10,77,78],{},"En esa secuencia no salió nada mal. La regla que añadiste hablaba de lectura, y\nguardar es otra pregunta que hasta entonces nadie había hecho.",[33,80,82],{"id":81},"una-regla-tiene-dos-mitades-y-solo-una-habla-de-lectura","Una regla tiene dos mitades, y solo una habla de lectura",[10,84,85],{},"Una policy es una condición, y dónde aplica la base de datos esa condición\ndepende de lo que le hayas pedido.",[10,87,88,91,92,95],{},[21,89,90],{},"USING"," se aplica a las filas que ya están en la tabla. Decide cuáles puedes\nver, cambiar o quitar. ",[21,93,94],{},"WITH CHECK"," se aplica a la fila que intentas crear,\nantes de que exista en ningún sitio. Decide si esa fila puede llegar a existir.",[10,97,98],{},"Esa segunda mitad es la que sorprende, porque es una regla sobre algo que\ntodavía no está ahí.",[100,101,102,118],"table",{},[103,104,105],"thead",{},[106,107,108,112,115],"tr",{},[109,110,111],"th",{},"Tu app pide",[109,113,114],{},"Se comprueba contra filas que ya están en la tabla",[109,116,117],{},"Se comprueba contra la fila que se escribe",[119,120,121,138,152,168],"tbody",{},[106,122,123,131,135],{},[124,125,126,127,130],"td",{},"leer (",[21,128,129],{},"select",")",[124,132,133],{},[21,134,90],{},[124,136,137],{},"nada que comprobar",[106,139,140,146,148],{},[124,141,142,143,130],{},"guardar una fila nueva (",[21,144,145],{},"insert",[124,147,137],{},[124,149,150],{},[21,151,94],{},[106,153,154,160,164],{},[124,155,156,157,130],{},"cambiar una fila (",[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],{},"quitar una fila (",[21,174,175],{},"delete",[124,177,178],{},[21,179,90],{},[124,181,137],{},[183,184],"diagram",{"alt":185,"caption":186,"src":187},"Una misma regla dibujada dos veces. A la izquierda apunta a una pila de filas que ya están dentro de una tabla y dos de ellas vuelven. A la derecha la misma regla apunta a una sola fila dibujada con su contorno, esperando fuera de la tabla, y la rechaza.","La misma condición, apuntando a dos cosas distintas. Al leer se le pregunta por filas que existen; al guardar, por una fila que todavía no existe.","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fusing-and-with-check-1600x780.png",[10,189,190,191,194],{},"Una policy escrita con ",[21,192,193],{},"FOR SELECT"," solo lleva la primera mitad, porque cuando\nalguien lee no hay ninguna fila nueva que comprobar. Así que nunca puede\npermitir un guardado, por permisiva que parezca. Ese es todo el desajuste, y\nexplica por qué tus lecturas se recuperaron y tus escrituras no.",[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"," se comporta distinto, y en silencio."," Si\nescribes una con condición ",[21,209,90],{}," y sin ",[21,212,94],{},", Postgres aplica esa misma\ncondición ",[21,215,90],{}," también a las filas nuevas. Así que una regla ",[21,218,205],{}," que\ndice que las filas pertenecen a su propietario rechaza un guardado en el que el\npropietario no coincide, sin que aparezca ningún ",[21,221,94],{}," en lo que\nescribiste. Útil cuando lo hiciste a propósito. Confuso cuando estás leyendo una\nregla que pegó otra persona.",[10,224,225,226,46],{},"Si prefieres verlo desde fuera, nuestro escaneo gratuito le pregunta a tu base\nde datos en vivo qué puede leer ya un desconocido, sin cuenta y en unos 20\nsegundos: ",[227,228,230],"a",{"href":229},"\u002F#scan","escanea tu app",[33,232,234],{"id":233},"el-arreglo-que-hace-parar-el-error-y-lo-que-cuesta","El arreglo que hace parar el error, y lo que cuesta",[10,236,237],{},"La forma más rápida de quitarlo es una regla que permita cualquier escritura de\ncualquiera, y por eso es la respuesta que encabeza casi todas las búsquedas.",[10,239,240],{},"Suele llegar con esta pinta:",[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],{},"Toda fila cumple ",[21,251,252],{},"true",", así que se permite cualquier guardado, el de tu app y\nel de cualquiera que tenga la clave que viaja dentro de ella. Volver a apagar\nRow Level Security hace lo mismo de forma aún más completa.",[10,255,256,257,261],{},"Las dos funcionan. Las dos te dejan donde estabas antes de que el advisor\nseñalara la tabla, y ninguna muestra después ninguna señal de que algo esté\nabierto, porque tu app se comporta igual en las cuatro combinaciones de ajuste y\nregla. Una tabla puede estar activada, llevar una policy válida, mostrar una\ninsignia verde en tu panel y aun así entregar sus filas a un desconocido. Esa es\n",[227,258,260],{"href":259},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","la otra mitad de este problema",",\ny merece leerse antes de pegar nada.",[33,263,265],{"id":264},"la-regla-que-deja-guardar-a-tu-app-y-solo-a-tu-app","La regla que deja guardar a tu app, y solo a tu app",[10,267,268],{},"Nombra a quién pertenece la fila, y compáralo con quien está pidiendo:",[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()"," es quien tiene la sesión iniciada y hace la petición. ",[21,282,283],{},"user_id"," es\nla columna de la fila que dice a quién pertenece. Cuando esos dos coinciden, la\nfila se guarda. Cuando no, o cuando no hay nadie con la sesión iniciada, se\nrechaza, y quien queda rechazado ve el mismo mensaje que llevas rato mirando.",[10,286,287,288,291,292,294,295,297],{},"Dos detalles de esa regla se ganan su sitio. ",[21,289,290],{},"TO authenticated"," significa que la\nregla se aplica a los visitantes con sesión iniciada; si lo quitas, la policy se\naplica a todo el mundo, desconocidos incluidos. Y tu app tiene que enviar de\nverdad la columna ",[21,293,283],{},", porque una regla que compara ",[21,296,279],{}," con un\nvalor que llegó vacío no puede coincidir nunca.",[33,299,301],{"id":300},"cuando-la-regla-está-bien-y-el-error-sigue-apareciendo","Cuando la regla está bien y el error sigue apareciendo",[10,303,304],{},"Mira quién tenía la sesión iniciada antes de volver a mirar la policy.",[183,306],{"alt":307,"caption":308,"src":309},"Tres tablas una al lado de otra. En cada una llega desde arriba una fila dibujada con su contorno y su flecha se detiene justo antes de la regla que hay debajo. La regla de la primera tabla es un contorno discontinuo vacío, la de la segunda lleva select y la de la tercera insert. Una franja recorre las tres por debajo con el código 42501.","Tres causas distintas que te llegan como una sola frase. El código 42501 es el mismo en las tres, y por eso el mensaje por sí solo no puede decirte en cuál estás.","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fthree-causes-one-message-1600x700.png",[10,311,312],{},"Tres explicaciones cubren casi todo lo que queda:",[10,314,315,318,319,321],{},[28,316,317],{},"No hay nadie con la sesión iniciada."," ",[21,320,279],{}," vuelve vacío para un\nvisitante que no ha entrado, así que una regla que lo compara con una columna de\npropietario no puede coincidir. Esto es normal en un formulario de registro, una\nlista de espera o un formulario de contacto, y esos necesitan su propia regla\nque describa qué puede añadir un desconocido.",[10,323,324,327,328,330,331,333,334,336],{},[28,325,326],{},"La columna de propietario nunca llega."," Tu regla compara ",[21,329,279],{}," con\n",[21,332,283],{},", y tu app envía todo menos ",[21,335,283],{},". La comparación corre contra un\nhueco y falla siempre.",[10,338,339,342,343,346,347,350],{},[28,340,341],{},"La escritura va a Storage."," Las subidas de archivos aterrizan en Supabase\nStorage, que lleva sus propias policies sobre ",[21,344,345],{},"storage.objects"," en vez de sobre\ntu tabla. Una regla escrita sobre ",[21,348,349],{},"profiles"," no dice nada sobre un archivo.",[10,352,353,354,358],{},"Hay una cuarta explicación, y es la que conviene descartar pronto: si guardar\nfunciona desde la vista previa de tu builder y falla desde tu sitio en vivo, los\ndos no están usando la misma clave. Una clave secreta ignora todas las reglas\nque hayas escrito, para eso está, así que una app que solo guarda mientras hay\nuna clave secreta de por medio es una app cuyas reglas nunca se han probado de\nverdad.\n",[227,355,357],{"href":356},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Qué claves de API son seguras en tu frontend","\nexplica cómo distinguir una de la otra.",[33,360,362],{"id":361},"qué-hacer-hoy","Qué hacer hoy",[364,365,366],"key-takeaways",{},[51,367,368,371,377,391,394],{},[54,369,370],{},"Lee el error como un rechazo y no como una avería. La escritura no ocurrió, la tabla está igual, y no hay nada que recuperar.",[54,372,373,374,376],{},"Comprueba si la tabla tiene alguna policy sobre escritura. Una regla escrita con ",[21,375,193],{}," arregló tus pantallas y no dijo nada sobre guardar.",[54,378,379,380,383,384,386,387,390],{},"Añade una policy ",[21,381,382],{},"FOR INSERT"," cuya condición ",[21,385,94],{}," nombre al propietario de la fila, y añade la cláusula ",[21,388,389],{},"TO"," que querías.",[54,392,393],{},"Confirma que tu app envía de verdad la columna de propietario, y prueba a guardar otra vez con la sesión iniciada.",[54,395,396,397,400],{},"Deja ",[21,398,399],{},"WITH CHECK (true)"," para tablas cuyo contenido publicarías en una página pública. Para cualquier cosa con una persona dentro, dedícale los dos minutos.",[10,402,403,404,408,409,413],{},"Empieza por la tabla de la que vino el error, y revisa las demás tablas en las\nque activaste el ajuste esa misma tarde. La\n",[227,405,407],{"href":406},"\u002Fchecklist","checklist de seguridad de 10 minutos"," cubre qué más suele quedarse\nabierto en una app recién lanzada, y la\n",[227,410,412],{"href":411},"\u002Fis-your-supabase-app-safe","guía de seguridad de Supabase"," repasa el resto de\nlo que puede alcanzar un desconocido.",{"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},"Fundamentos de seguridad","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcover-1200x630.png","Una pila de filas dentro de una tabla, con una fila más esperando fuera, dibujada solo con su contorno porque no la han dejado entrar.","\"New row violates row-level security policy\" significa que Supabase rechazó una escritura. El arreglo rápido vuelve a abrir la tabla para cualquiera.",false,"md",[432,435,438,441,444],{"q":433,"a":434},"¿Qué significa \"new row violates row-level security policy\"?","Significa que se le pidió a tu base de datos guardar una fila, comprobó las reglas de esa tabla y no encontró ninguna que dejara entrar esa fila. Así que rechazó la escritura. No se perdió nada y nada está roto, porque la fila nunca llegó a guardarse y el resto de la tabla sigue intacto. El mensaje viene de Postgres, el motor de base de datos sobre el que corre Supabase, y lleva el código 42501. Lo que deliberadamente no te dice es qué regla faltaba, porque decirlo describiría tus reglas a quien haya provocado el error.",{"q":436,"a":437},"Añadí una policy y la lectura funciona. ¿Por qué sigue fallando al guardar?","Porque una policy sobre lectura no dice nada sobre escritura. Una regla lleva una mitad USING, que la base de datos aplica a las filas que ya están en la tabla, y una mitad WITH CHECK, que aplica a la fila que intentas crear. Una policy escrita con FOR SELECT solo tiene la primera, porque al leer no hay ninguna fila nueva que comprobar. Tus pantallas vuelven a llenarse y el primer guardado sigue fallando. Añade una segunda policy FOR INSERT con una condición WITH CHECK.",{"q":439,"a":440},"¿Y si simplemente apago Row Level Security para que esto desaparezca?","Eso quita el error, y deja cada fila de esa tabla legible por cualquiera que tenga la clave que viaja dentro de tu app. Tu app funciona de las dos formas, así que después nada te dice cuál de las dos elegiste. Si la tabla guarda personas, pedidos o mensajes, los dos minutos que cuesta escribir una regla de verdad son la diferencia entre una tabla privada y una pública.",{"q":442,"a":443},"Mi policy parece correcta y los inserts siguen fallando. ¿Qué más puede ser?","Tres cosas explican la mayoría de los casos. No hay nadie con la sesión iniciada, así que auth.uid() viene vacío y una regla que lo compara con una columna de propietario no puede coincidir nunca, lo cual es normal en un formulario de registro o de contacto. O tu app no envía la columna de propietario, así que la regla compara contra un valor que llegó en blanco. O la escritura va a Supabase Storage en vez de a una tabla, y Storage lleva sus propias policies sobre storage.objects.",{"q":445,"a":446},"¿Este error significa que alguien intentó atacar mi app?","Casi nunca. En una app recién lanzada casi siempre es tu propia app la que está siendo rechazada, porque Row Level Security se activó antes de que existiera una regla que cubriera tus propias escrituras. Aun así conviene leerlo en vez de descartarlo: el mismo mensaje aparece cuando se rechaza una petición que no debería estar escribiendo en esa tabla, y por el mensaje solo no puedes distinguir una de otra.","\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 bloquea insert","supabase with check policy","activar row level security supabase","error row level security",{},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 significa que tu base de datos comprobó las reglas de esa tabla y no encontró ninguna que permitiera la fila que estabas guardando. Rechazó la escritura y dejó la tabla como estaba.","Un solo mensaje cubre tres situaciones distintas: ninguna regla, una regla que solo habla de lectura, o una regla de escritura que tu fila no cumple.","La respuesta que encabeza cualquier búsqueda hace que el error pare permitiendo cualquier escritura de cualquiera. La regla que de verdad quieres cuesta unos dos minutos más.","SWhaX4mEngGioEHOo1yhOZRXGmI-PMTlS7FK8I9HOPc",1787826048205]