[{"data":1,"prerenderedAt":1328},["ShallowReactive",2],{"blog-es-invalid-input-syntax-for-type-uuid":3,"blog-index-es":798},{"id":4,"title":5,"body":6,"category":755,"cover":756,"coverAlt":757,"description":758,"draft":759,"extension":760,"faq":761,"image":777,"keywords":778,"meta":787,"navigation":788,"ogTitle":5,"path":789,"published":790,"seo":791,"stem":792,"tldr":793,"updated":790,"__hash__":797},"blog_es\u002Fblog\u002Finvalid-input-syntax-for-type-uuid.md","Supabase \"invalid input syntax for type uuid\": lee las comillas",{"type":7,"value":8,"toc":743},"minimark",[9,13,24,31,37,42,49,83,86,189,193,196,326,329,333,339,384,394,400,403,455,459,468,476,486,507,513,516,521,525,528,541,557,562,566,569,606,614,618,621,630,650,659,677,681,736],[10,11,12],"p",{},"Abres el panel de tu app y la lista de proyectos se queda vacía un momento y\nluego se llena. O se queda vacía. En cualquier caso, la consola del navegador\ntiene una línea roja, y la petición que hay detrás respondió con esto:",[14,15,20],"pre",{"className":16,"code":18,"language":19},[17],"language-text","{\n  \"code\": \"22P02\",\n  \"details\": null,\n  \"hint\": null,\n  \"message\": \"invalid input syntax for type uuid: \\\"undefined\\\"\"\n}\n","text",[21,22,18],"code",{"__ignoreMap":23},"",[10,25,26,27,30],{},"Supabase \"invalid input syntax for type uuid\" significa que Postgres recibió un\nvalor que no es un uuid, para una columna que solo guarda uuids, y rechazó la\npetición entera. Lo que va entre comillas es lo que envió tu app. Aquí es la\npalabra ",[21,28,29],{},"undefined",": la consulta se ejecutó antes de que existiera el id que\nnecesitaba.",[10,32,33,34,36],{},"Los arreglos rápidos que circulan para este error son cambiar la columna a ",[21,35,19],{},"\no convertir el valor. Los dos hacen desaparecer el mensaje. Ninguno le da a la\nconsulta su id, así que la página sigue pidiendo lo que no es y deja de avisarte.",[38,39,41],"h2",{"id":40},"qué-significa-invalid-input-syntax-for-type-uuid","¿Qué significa \"invalid input syntax for type uuid\"?",[10,43,44,45,48],{},"Significa que un valor que iba hacia una columna ",[21,46,47],{},"uuid"," no estaba escrito como se\nescribe un uuid, así que Postgres se detuvo antes de ejecutar nada. No se leyó\nninguna fila y no se cambió ninguna.",[10,50,51,52,55,56,59,60,63,64,67,68,71,72,75,76,82],{},"Un uuid es un id con una forma fija: 32 caracteres del ",[21,53,54],{},"0"," al ",[21,57,58],{},"9"," y de la ",[21,61,62],{},"a"," a\nla ",[21,65,66],{},"f",", escritos en grupos de 8, 4, 4, 4 y 12, como\n",[21,69,70],{},"3f2b8c1e-9a4d-4e7b-b1c2-5d6e7f8a9b0c",". Supabase da a cada usuario con sesión un\nid así, y una columna que registra de quién es cada fila, como ",[21,73,74],{},"user_id",", guarda\nel mismo tipo de valor. El\n",[62,77,81],{"href":78,"rel":79},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Fadt\u002Fuuid.c",[80],"nofollow","lector de Postgres para ese tipo","\nacepta \"a series of 32 hexadecimal digits with an optional dash after each group\nof 4 hexadecimal digits, and optionally surrounded by {}\". Cualquier otra cosa\ntermina en este mensaje, con la entrada citada.",[10,84,85],{},"Piensa en la columna como una máquina clasificadora que solo lee un tipo de\netiqueta: un código en esos cinco grupos. Tu app puso en la cinta un paquete con\nuna palabra escrita donde va el código. La máquina paró la cinta y te leyó la\npalabra.",[87,88,89,105],"table",{},[90,91,92],"thead",{},[93,94,95,99,102],"tr",{},[96,97,98],"th",{},"Campo",[96,100,101],{},"Ejemplo",[96,103,104],{},"Lo que te dice",[106,107,108,132,147,166],"tbody",{},[93,109,110,115,120],{},[111,112,113],"td",{},[21,114,21],{},[111,116,117],{},[21,118,119],{},"22P02",[111,121,122,123,126,127],{},"El código de Postgres para este error, llamado ",[21,124,125],{},"invalid_text_representation"," en ",[62,128,131],{"href":129,"rel":130},"https:\u002F\u002Fgithub.com\u002Fpostgres\u002Fpostgres\u002Fblob\u002Fmaster\u002Fsrc\u002Fbackend\u002Futils\u002Ferrcodes.txt",[80],"su lista de códigos",[93,133,134,139,144],{},[111,135,136],{},[21,137,138],{},"message",[111,140,141],{},[21,142,143],{},"invalid input syntax for type uuid: \"undefined\"",[111,145,146],{},"El tipo que Postgres esperaba y el valor exacto que recibió",[93,148,149,158,163],{},[111,150,151,154,155],{},[21,152,153],{},"details",", ",[21,156,157],{},"hint",[111,159,160],{},[21,161,162],{},"null",[111,164,165],{},"Postgres no añade nada más para este error",[93,167,168,171,176],{},[111,169,170],{},"Estado HTTP",[111,172,173],{},[21,174,175],{},"400 Bad Request",[111,177,178,179,182,183,188],{},"Cómo responde PostgREST, la parte de Supabase que convierte las llamadas ",[21,180,181],{},"supabase.from(...)"," en SQL, a cualquier código sin un estado propio (",[62,184,187],{"href":185,"rel":186},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FError.hs",[80],"su código",")",[38,190,192],{"id":191},"lee-lo-que-hay-entre-las-comillas","Lee lo que hay entre las comillas",[10,194,195],{},"El valor citado es la mitad útil del mensaje. Es exactamente lo que llegó a la\nbase de datos, y cada forma que toma apunta a una línea distinta de tu app.",[87,197,198,211],{},[90,199,200],{},[93,201,202,205,208],{},[96,203,204],{},"Entre las comillas",[96,206,207],{},"Lo que envió tu app",[96,209,210],{},"Dónde mirar",[106,212,213,226,243,270,290,307],{},[93,214,215,220,223],{},[111,216,217],{},[21,218,219],{},"\"undefined\"",[111,221,222],{},"una variable que aún no tenía valor",[111,224,225],{},"una consulta que se ejecutó antes de que cargara el usuario o el id de la página; las dos secciones siguientes",[93,227,228,233,236],{},[111,229,230],{},[21,231,232],{},"\"\"",[111,234,235],{},"una cadena vacía",[111,237,238,239,242],{},"un valor que empieza como ",[21,240,241],{},"''"," en el estado de tu app, o un campo de formulario en blanco",[93,244,245,250,258],{},[111,246,247],{},[21,248,249],{},"\"null\"",[111,251,252,254,255],{},[21,253,162],{},", pasado a ",[21,256,257],{},".eq()",[111,259,260,262,263,265,266,269],{},[21,261,257],{}," también escribe ",[21,264,162],{}," como palabra. Para encontrar filas en las que la columna está vacía, la documentación de supabase-js dice \"use ",[21,267,268],{},".is()"," instead\"",[93,271,272,277,280],{},[111,273,274],{},[21,275,276],{},"\"[object Object]\"",[111,278,279],{},"un objeto entero donde iba su id",[111,281,282,285,286,289],{},[21,283,284],{},"user"," en lugar de ",[21,287,288],{},"user.id",". JavaScript escribe ese texto para cualquier objeto convertido en cadena",[93,291,292,297,300],{},[111,293,294],{},[21,295,296],{},"\"user_…\"",[111,298,299],{},"un id de un servicio de inicio de sesión que no es el de Supabase",[111,301,302,303,306],{},"Los ids de Clerk empiezan por ",[21,304,305],{},"user_"," y no son uuids. La sección sobre cambiar la columna trata este caso",[93,308,309,312,315],{},[111,310,311],{},"un número o un nombre",[111,313,314],{},"parte de la dirección de la página",[111,316,317,318,321,322,325],{},"una URL como ",[21,319,320],{},"\u002Fprojects\u002F42"," o ",[21,323,324],{},"\u002Fprojects\u002Fmy-first-app",", leída como id",[10,327,328],{},"Si el error aparece en cada visita, busca un nombre que no contiene nada. Si solo\naparece la primera vez que se dibuja la página, busca un valor que aún no había\ncargado.",[38,330,332],{"id":331},"por-qué-supabase-js-envía-la-palabra-undefined","¿Por qué supabase-js envía la palabra undefined?",[10,334,335,336,338],{},"Porque escribe cada filtro como texto en la dirección de la petición, y\nJavaScript escribe un valor que falta como la palabra ",[21,337,29],{},".",[10,340,341,342,345,346,349,350,355,356,359,360,363,364,367,368,373,374,376,377,380,381,383],{},"Una llamada como ",[21,343,344],{},".eq('user_id', user?.id)"," se convierte en ",[21,347,348],{},"?user_id=eq.","\nseguido del valor. El\n",[62,351,354],{"href":352,"rel":353},"https:\u002F\u002Fgithub.com\u002Fsupabase\u002Fsupabase-js\u002Fblob\u002Fmaster\u002Fpackages\u002Fcore\u002Fpostgrest-js\u002Fsrc\u002FPostgrestFilterBuilder.ts",[80],"código fuente de la librería","\nlo construye con una plantilla, ",[21,357,358],{},"`eq.${value}`",", y un comentario más abajo en\nel mismo archivo dice qué hace eso con un valor que falta: \"show up as\n",[21,361,362],{},"?column=eq.undefined","\". PostgREST lee todo lo que sigue a ",[21,365,366],{},"eq."," como texto\nplano, y\n",[62,369,372],{"href":370,"rel":371},"https:\u002F\u002Fgithub.com\u002FPostgREST\u002Fpostgrest\u002Fblob\u002Fmain\u002Fsrc\u002Flibrary\u002FPostgREST\u002FApiRequest\u002FQueryParams.hs",[80],"su parser","\nsolo trata ",[21,375,162],{}," como palabra clave después de ",[21,378,379],{},"is.",". Postgres recibe las nueve\nletras de ",[21,382,29],{}," e intenta leerlas como un uuid.",[10,385,386,389,390,338],{},[21,387,388],{},".rpc()"," va al revés. Sus argumentos viajan como JSON, que deja fuera por completo\nun valor undefined, así que una llamada a una función pierde el argumento en vez\nde enviar la palabra, y Supabase responde\n",[62,391,393],{"href":392},"\u002Fblog\u002Fsupabase-function-not-found","Could not find the function",[395,396],"diagram",{"alt":397,"caption":398,"src":399},"Una ficha de filtro con user_id y una caja ámbar que contiene undefined. Una flecha lleva a una tira de petición que termina en eq.undefined, luego a la palabra entre comillas en una tarjeta sobre una casilla de código de cinco cajas en la que no cabe, y abajo a una placa con 22P02 y 400.","supabase-js mete el valor del filtro en la petición como texto. Un valor que falta llega como la palabra undefined, y la columna uuid lo rechaza.","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fundefined-becomes-a-word-1600x560.png",[10,401,402],{},"En una app de React, busca el valor que falta en tres sitios:",[404,405,406,428,449],"ul",{},[407,408,409,413,414,417,418,420,421,424,425,427],"li",{},[410,411,412],"strong",{},"El usuario aún no ha cargado."," Supabase lee la sesión iniciada de forma\nasíncrona (",[21,415,416],{},"await supabase.auth.getSession()","), así que la primera vez que se\ndibuja tu página, un ",[21,419,284],{}," guardado en el estado sigue vacío y ",[21,422,423],{},"user?.id"," es\n",[21,426,29],{},". Una consulta lanzada en ese primer dibujado lo envía.",[407,429,430,433,434,437,438,441,442,445,446,448],{},[410,431,432],{},"El id tiene otro nombre."," Una ruta escrita como ",[21,435,436],{},"\u002Fprojects\u002F:projectId"," pone\nel valor bajo ",[21,439,440],{},"projectId",", y una página que lee ",[21,443,444],{},"id"," recibe ",[21,447,29],{}," en cada\nvisita.",[407,450,451,454],{},[410,452,453],{},"El id viene de datos que aún están en camino."," Un id sacado del resultado de\notra consulta, leído antes de que esa consulta haya respondido.",[38,456,458],{"id":457},"frena-la-consulta-hasta-que-exista-el-id","Frena la consulta hasta que exista el id",[10,460,461,462,464,465,467],{},"Comprueba el id antes de la llamada y deja que la página muestre su estado de\ncarga hasta que llegue. Para ",[21,463,219],{}," y ",[21,466,232],{}," ese es todo el arreglo.",[14,469,474],{"className":470,"code":472,"language":473,"meta":23},[471],"language-js","useEffect(() => {\n  if (!user?.id) return\n\n  supabase\n    .from('projects')\n    .select('*')\n    .eq('user_id', user.id)\n    .then(({ data, error }) => {\n      if (!error) setProjects(data)\n    })\n}, [user?.id])\n","js",[21,475,472],{"__ignoreMap":23},[10,477,478,479,482,483,485],{},"El ",[21,480,481],{},"return"," no envía nada mientras falta el id, y ",[21,484,423],{}," en la lista del\nfinal vuelve a ejecutar el efecto en cuanto aparece. En la máquina clasificadora,\neso es esperar a que el código esté en la etiqueta antes de poner el paquete en\nla cinta.",[10,487,488,489,492,493,496,497,502,503,506],{},"Si tu app carga datos con TanStack Query (el hook ",[21,490,491],{},"useQuery","), su opción\n",[21,494,495],{},"enabled"," es el mismo freno.\n",[62,498,501],{"href":499,"rel":500},"https:\u002F\u002Ftanstack.com\u002Fquery\u002Flatest\u002Fdocs\u002Fframework\u002Freact\u002Fguides\u002Fdependent-queries",[80],"Su guía de consultas dependientes","\npone ",[21,504,505],{},"enabled: !!userId"," con la nota \"The query will not execute until the userId\nexists\".",[14,508,511],{"className":509,"code":510,"language":473,"meta":23},[471],"const { data: projects } = useQuery({\n  queryKey: ['projects', user?.id],\n  queryFn: async () => {\n    const { data, error } = await supabase\n      .from('projects')\n      .select('*')\n      .eq('user_id', user.id)\n    if (error) throw error\n    return data\n  },\n  enabled: !!user?.id,\n})\n",[21,512,510],{"__ignoreMap":23},[10,514,515],{},"Si construyes con prompts, pídelo en una frase. Déjala en inglés, que es el\nidioma en el que piensan estas herramientas: \"Don't run the projects query until\nuser.id exists, and show the loading state until then.\"",[395,517],{"alt":518,"caption":519,"src":520},"Dos filas, cada una con dos dibujados numerados de una página. En la fila de arriba el primer dibujado tiene el id vacío y su petición termina en una placa ámbar 22P02; el segundo tiene el id y termina en una marca de visto. En la fila de abajo, marcada con if (!id) return, el primer dibujado se detiene en un signo de pausa y no envía nada, y solo el segundo envía una petición, que termina en una marca de visto.","Sin la comprobación, el primer dibujado envía una petición sin id. Con ella, el primer dibujado espera, y la única petición enviada es la que funciona.","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fthe-guard-holds-the-query-1600x640.png",[38,522,524],{"id":523},"deberías-cambiar-la-columna-a-text","¿Deberías cambiar la columna a text?",[10,526,527],{},"No, salvo que los ids que guardas no sean uuids en absoluto. Cambiar la columna,\no convertirla a text en una consulta, quita la comprobación que lanzó el error y\ndeja el id que falta donde estaba.",[10,529,530,531,534,535,537,538,540],{},"Sigue la misma petición después del cambio. ",[21,532,533],{},"user_id=eq.undefined"," ahora es una\npregunta válida, así que Postgres compara la palabra ",[21,536,29],{}," con cada id de la\ncolumna, no encuentra ninguna coincidencia y devuelve una lista vacía. Tu página\nmuestra \"Aún no hay proyectos\" a alguien que tiene diez, y en la consola no hay\nnada que lo explique. Al escribir va más lejos: la fila se guarda con ",[21,539,29],{},"\ndonde va el id de su dueño, y una consulta del dueño real no la encuentra nunca.\nEn la máquina clasificadora, eso es desenchufar el lector de etiquetas. El\npaquete pasa, no coincide con ninguna dirección y nunca se entrega.",[10,542,543,544,546,547,552,553,556],{},"El único caso en que lo que hay que cambiar es la columna es la fila ",[21,545,296],{},"\nde la tabla de arriba. Los ids de Clerk son texto, y\n",[62,548,551],{"href":549,"rel":550},"https:\u002F\u002Fclerk.com\u002Fdocs\u002Fguides\u002Fdevelopment\u002Fintegrations\u002Fdatabases\u002Fsupabase",[80],"la propia guía de Clerk para Supabase","\ncrea la columna como ",[21,554,555],{},"user_id text not null default auth.jwt()->>'sub'",". Es otro\nproblema: la tabla se hizo para los ids de Supabase y está recibiendo los de otro\nservicio.",[395,558],{"alt":559,"caption":560,"src":561},"La misma petición, user_id=eq.undefined, enviada a dos columnas. La columna marcada uuid la rechaza con una placa ámbar que dice 22P02 y 400. La columna marcada text compara la palabra con cada fila, no coincide con ninguna y devuelve una lista vacía, una caja ámbar discontinua junto a un 200 sin más.","Una columna uuid rechaza la palabra y lo dice. Una columna text la acepta, no encuentra ninguna fila y le entrega a tu página una lista vacía sin error.","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fa-text-column-hides-it-1600x660.png",[38,563,565],{"id":564},"es-el-id-de-tu-filtro-lo-que-deja-fuera-las-filas-ajenas","¿Es el id de tu filtro lo que deja fuera las filas ajenas?",[10,567,568],{},"No. El filtro decide qué filas pide tu página. Qué filas puede leer un visitante\nlo decide Row Level Security, las reglas por fila que Supabase comprueba en cada\npetición hecha con tu clave pública, y cualquiera puede volver a enviar tu\npetición con otro id o sin ninguno.",[10,570,571,572,575,576,580,581,583,584,587,588,591,592,595,596,600,601,605],{},"Un método de supabase-js muestra la diferencia. ",[21,573,574],{},".match()"," recibe varias columnas\na la vez, y\n",[62,577,579],{"href":352,"rel":578},[80],"su código fuente","\nquita cualquier columna cuyo valor sea ",[21,582,29],{}," antes de construir la petición.\nPara eso estaba aquel comentario sobre ",[21,585,586],{},"eq.undefined",". Así que un\n",[21,589,590],{},".match({ user_id: user?.id })"," hecho antes de que cargue el usuario no envía\nningún filtro, no da ningún error y devuelve todas las filas que las reglas de la\ntabla le dejan leer a ese visitante. Añade ",[21,593,594],{},".single()"," a esa llamada y falla de\notra manera, con\n",[62,597,599],{"href":598},"\u002Fblog\u002Fsupabase-single-row-error","JSON object requested, multiple (or no) rows returned",",\nporque volvió más de una fila. Con las reglas activadas, esas filas siguen siendo\nsolo las del visitante. Con ellas desactivadas, son la tabla entera, y\n",[62,602,604],{"href":603},"\u002Fblog\u002Fenable-rls-on-every-supabase-table","activarlas en cada tabla"," explica el\ninterruptor y la primera regla.",[10,607,608,609,613],{},"Si una tabla le entrega filas a un desconocido se puede ver desde fuera. Nuestro\n",[62,610,612],{"href":611},"\u002Fsecurity-scanner","escaneo gratuito"," lee tu app en vivo con la misma clave\npública que recibe un visitante, informa de qué tablas responden con filas y no\nnecesita cuenta.",[38,615,617],{"id":616},"antes-de-cambiar-una-columna-que-lee-tu-app","Antes de cambiar una columna que lee tu app",[10,619,620],{},"Guarda una copia de la base de datos de antes de cambiar el tipo de una columna,\ny entérate cuando una tabla empiece a responder a desconocidos.",[10,622,623],{},[410,624,625,629],{},[62,626,628],{"href":627},"\u002Fsupabase-backups","Reeve Care"," guarda una copia de tu base de datos de Supabase:",[404,631,632,635,638,641,644,647],{},[407,633,634],{},"una copia cifrada según un calendario, en el almacenamiento propio de Reeve en la UE, fuera de tu cuenta de Supabase",[407,636,637],{},"cada tabla con sus filas tal como estaban antes de que cambiaras el tipo de una columna",[407,639,640],{},"cada copia verificada antes de contar, contando las filas de cada tabla",[407,642,643],{},"también tus archivos subidos, en cuanto conectas una credencial de Storage",[407,645,646],{},"una restauración con un clic, con una copia de seguridad del estado actual tomada antes",[407,648,649],{},"todo lo que hace Monitor",[10,651,652],{},[410,653,654,658],{},[62,655,657],{"href":656},"\u002Fwebsite-monitoring","Reeve Monitor"," sigue comprobando desde fuera:",[404,660,661,664,667,674],{},[407,662,663],{},"si tu app está en línea, comprobado cada minuto",[407,665,666],{},"la comprobación de seguridad completa cada hora, en hasta tres apps",[407,668,669,670,673],{},"un mensaje cuando un resultado ",[410,671,672],{},"cambia",", para que una tabla que empieza a responder a desconocidos tras un despliegue te llegue sin que tengas que ir a mirar",[407,675,676],{},"un informe mensual, en lenguaje claro, de lo que vio",[38,678,680],{"id":679},"qué-hacer-ahora","Qué hacer ahora",[682,683,684],"key-takeaways",{},[404,685,686,689,705,713,721,730],{},[407,687,688],{},"Lee el valor entre las comillas. Es exactamente lo que envió tu app.",[407,690,691,692,321,694,696,697,700,701,704],{},"Con ",[21,693,219],{},[21,695,232],{},", frena la consulta hasta que exista el id: ",[21,698,699],{},"if (!user?.id) return",", o ",[21,702,703],{},"enabled: !!user?.id"," en TanStack Query.",[407,706,691,707,709,710,338],{},[21,708,249],{},", encuentra los valores vacíos con ",[21,711,712],{},".is('column', null)",[407,714,691,715,717,718,720],{},[21,716,276],{},", pasa el ",[21,719,444],{}," del objeto.",[407,722,723,724,726,727,729],{},"Deja la columna como ",[21,725,47],{}," y cámbiala a ",[21,728,19],{}," solo cuando los ids vengan de un servicio que no emite uuids, como Clerk.",[407,731,732,733,735],{},"Si una llamada ",[21,734,574],{}," con un valor que falta devuelve filas de otras personas, activa Row Level Security en esa tabla.",[10,737,738,739,742],{},"Empieza por las comillas, porque el valor que contienen apunta a la línea de tu\napp que lo envió. Si el arreglo que te proponen cambia el tipo de una columna,\n",[62,740,741],{"href":627},"guarda una copia de la base de datos"," de antes del cambio.",{"title":23,"searchDepth":744,"depth":744,"links":745},3,[746,748,749,750,751,752,753,754],{"id":40,"depth":747,"text":41},2,{"id":191,"depth":747,"text":192},{"id":331,"depth":747,"text":332},{"id":457,"depth":747,"text":458},{"id":523,"depth":747,"text":524},{"id":564,"depth":747,"text":565},{"id":616,"depth":747,"text":617},{"id":679,"depth":747,"text":680},"Fundamentos de seguridad","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcover-1200x630.png","Una etiqueta de paquete con una casilla de código partida en cinco cajas y, delante, una tarjeta con undefined en ámbar, demasiado larga.","Supabase \"invalid input syntax for type uuid\" significa que tu app envió algo que no es un id. Lee las comillas, frena la consulta, conserva la columna.",false,"md",[762,765,768,771,774],{"q":763,"a":764},"¿Qué significa invalid input syntax for type uuid?","Significa que Postgres tenía que leer un valor como uuid y el valor no está escrito como uno, así que rechazó la petición antes de leer o cambiar nada. El código es 22P02, una app de Supabase lo recibe como HTTP 400, y el texto entre comillas al final del mensaje es exactamente lo que envió tu app.",{"q":766,"a":767},"¿Por qué mi id es undefined?","Porque la consulta se ejecutó antes de que existiera el valor, o porque lee un nombre que no contiene nada. La primera vez que se dibuja una página puede que el usuario con sesión aún no se haya leído, así que user?.id es undefined, y una ruta que llama al valor projectId le da undefined, siempre, a una página que lee id. supabase-js escribe el valor que falta en la petición como la palabra undefined.",{"q":769,"a":770},"¿Debo cambiar la columna a text o convertir el valor?","No para arreglar esto. Una columna text acepta la palabra undefined, la compara con cada id, no encuentra ninguno, y tu página muestra una lista vacía sin ningún error. Cambia la columna solo cuando los ids que guardas no son uuids, como los ids de usuario de Clerk, que la propia guía de Clerk para Supabase guarda en una columna text.",{"q":772,"a":773},"¿Por qué solo pasa en la primera carga?","Porque en el siguiente dibujado el id ya llegó y la misma consulta funciona. La página puede llenarse un momento después y parecer correcta, mientras cada visita sigue enviando una petición fallida con undefined dentro. Frena la consulta hasta que exista el id y esa petición no se envía nunca.",{"q":775,"a":776},"¿Cómo evito que la consulta se ejecute demasiado pronto?","Sal antes de la llamada mientras falte el id, por ejemplo con if (!user?.id) return al principio del efecto, y pon user?.id como dependencia del efecto para que vuelva a ejecutarse cuando aparezca el id. Con TanStack Query, usa enabled: !!user?.id. Mientras tanto, muestra tu estado de carga.","\u002Fblog\u002Finvalid-input-syntax-for-type-uuid\u002Fcard-800x500.png",[779,780,781,782,783,784,785,786],"invalid input syntax for type uuid","invalid input syntax for type uuid undefined","supabase error uuid","postgres 22p02","supabase uuid inválido","supabase id undefined","invalid input syntax for type uuid null","supabase eq undefined",{},true,"\u002Fblog\u002Finvalid-input-syntax-for-type-uuid","2026-10-25",{"title":5,"description":758},"blog\u002Finvalid-input-syntax-for-type-uuid",[794,795,796],"Supabase \"invalid input syntax for type uuid\" (código 22P02 de Postgres, HTTP 400 en tu app) significa que una columna uuid recibió un valor que no es un uuid. El texto entre comillas es exactamente lo que envió tu app.","\"undefined\" entre las comillas significa que la consulta se ejecutó antes de que existiera el id, porque supabase-js escribe un valor que falta como esa palabra dentro de la petición. Frena la consulta hasta que el id esté.","Deja la columna como uuid. Cambiarla a text convierte un error ruidoso en una página que, en silencio, no muestra nada.","vCeKc4X_MA9ZbTNCOtTy27g9liGG6hNkIv_JdcSCA94",[799,805,811,817,823,829,835,841,847,853,859,866,872,878,884,890,891,897,903,908,914,920,926,932,938,944,949,955,961,967,973,979,985,991,997,1003,1009,1015,1021,1027,1033,1039,1045,1051,1057,1063,1068,1074,1080,1086,1092,1098,1104,1110,1116,1122,1128,1134,1140,1146,1152,1158,1163,1169,1175,1181,1187,1193,1199,1205,1211,1217,1223,1229,1235,1241,1247,1253,1259,1265,1271,1277,1283,1289,1294,1300,1306,1312,1317,1323],{"path":800,"title":801,"description":802,"published":803,"category":755,"image":804,"draft":759},"\u002Fblog\u002Fmax-client-connections-reached","Supabase \"max client connections reached\", explicado","Supabase \"max client connections reached\": el pooler no tiene sitio para un cliente más. El puerto de tu cadena de conexión decide el límite.","2026-11-09","\u002Fblog\u002Fmax-client-connections-reached\u002Fcard-800x500.png",{"path":806,"title":807,"description":808,"published":809,"category":755,"image":810,"draft":759},"\u002Fblog\u002Funsupported-provider-provider-is-not-enabled","Error de Supabase: \"Unsupported provider: provider is not enabled\"","\"Unsupported provider: provider is not enabled\": Google está desactivado en tu proyecto Supabase. Cómo activarlo y arreglar redirect_uri_mismatch.","2026-11-08","\u002Fblog\u002Funsupported-provider-provider-is-not-enabled\u002Fcard-800x500.png",{"path":812,"title":813,"description":814,"published":815,"category":755,"image":816,"draft":759},"\u002Fblog\u002Fviolates-foreign-key-constraint","Error de Supabase \"violates foreign key constraint\" al insertar","Supabase \"insert or update on table violates foreign key constraint\": tu fila apunta a una fila que no existe. Cuál, por qué, y cómo arreglarlo.","2026-11-07","\u002Fblog\u002Fviolates-foreign-key-constraint\u002Fcard-800x500.png",{"path":818,"title":819,"description":820,"published":821,"category":755,"image":822,"draft":759},"\u002Fblog\u002Fnull-value-violates-not-null-constraint","Error de Supabase \"null value in column violates not-null constraint\"","Supabase \"null value in column violates not-null constraint\": una columna obligatoria llegó vacía. De dónde vino el null, y cómo arreglarlo.","2026-11-06","\u002Fblog\u002Fnull-value-violates-not-null-constraint\u002Fcard-800x500.png",{"path":824,"title":825,"description":826,"published":827,"category":755,"image":828,"draft":759},"\u002Fblog\u002Fauth-session-missing","Supabase \"Auth session missing!\": adónde fue la sesión","\"Auth session missing!\" es supabase-js sin encontrar ninguna sesión iniciada y sin llegar a preguntar a Supabase. Adónde fue la sesión, y el arreglo.","2026-11-05","\u002Fblog\u002Fauth-session-missing\u002Fcard-800x500.png",{"path":830,"title":831,"description":832,"published":833,"category":755,"image":834,"draft":759},"\u002Fblog\u002Finvalid-claim-missing-sub-claim","Supabase \"invalid claim: missing sub claim\": no hay nadie conectado","\"invalid claim: missing sub claim\": Supabase Auth recibió un token auténtico sin ningún usuario, la clave del proyecto en lugar de una sesión.","2026-11-04","\u002Fblog\u002Finvalid-claim-missing-sub-claim\u002Fcard-800x500.png",{"path":836,"title":837,"description":838,"published":839,"category":755,"image":840,"draft":759},"\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function","\"Failed to send a request to the Edge Function\": la causa real","\"Failed to send a request to the Edge Function\": el navegador no recibió una respuesta legible. Su consola dice por qué y si tu función se ejecutó.","2026-11-03","\u002Fblog\u002Ffailed-to-send-a-request-to-the-edge-function\u002Fcard-800x500.png",{"path":842,"title":843,"description":844,"published":845,"category":755,"image":846,"draft":759},"\u002Fblog\u002Fsupabase-auth-redirects-to-localhost","Por qué los enlaces de email de Supabase redirigen a localhost:3000","Si Supabase redirige a localhost:3000, la Site URL nunca se cambió o tu dirección falta en Redirect URLs. La solución, y el comodín que debes evitar.","2026-11-02","\u002Fblog\u002Fsupabase-auth-redirects-to-localhost\u002Fcard-800x500.png",{"path":848,"title":849,"description":850,"published":851,"category":755,"image":852,"draft":759},"\u002Fblog\u002Femail-link-is-invalid-or-has-expired","Supabase \"Email link is invalid or has expired\" con un enlace nuevo","Con un enlace enviado hace un minuto, \"Email link is invalid or has expired\" en Supabase suele indicar que un filtro de correo lo usó antes. Cómo arreglarlo.","2026-11-01","\u002Fblog\u002Femail-link-is-invalid-or-has-expired\u002Fcard-800x500.png",{"path":854,"title":855,"description":856,"published":857,"category":755,"image":858,"draft":759},"\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout","Supabase \"canceling statement due to statement timeout\", explicado","Supabase \"canceling statement due to statement timeout\": una consulta superó el límite de su rol, 3 segundos para un visitante. Revisa antes la política RLS.","2026-10-31","\u002Fblog\u002Fcanceling-statement-due-to-statement-timeout\u002Fcard-800x500.png",{"path":860,"title":861,"description":862,"published":863,"category":864,"image":865,"draft":759},"\u002Fblog\u002Fdatabase-error-deleting-user","\"Database error deleting user\" en Supabase, y qué se va con él","\"Database error deleting user\" en Supabase: tu base de datos rechazó el borrado. La causa está en tus logs y el arreglo decide qué más se borra.","2026-10-30","Backups","\u002Fblog\u002Fdatabase-error-deleting-user\u002Fcard-800x500.png",{"path":867,"title":868,"description":869,"published":870,"category":755,"image":871,"draft":759},"\u002Fblog\u002Fstripe-webhook-no-signatures-found","\"No signatures found matching the expected signature\" en Supabase","¿Tu webhook de Stripe en una Edge Function de Supabase dice \"No signatures found matching the expected signature\"? Tiene dos causas. Sigue verificando.","2026-10-29","\u002Fblog\u002Fstripe-webhook-no-signatures-found\u002Fcard-800x500.png",{"path":873,"title":874,"description":875,"published":876,"category":755,"image":877,"draft":759},"\u002Fblog\u002Fsupabase-invalid-login","\"Invalid login credentials\" en Supabase: las tres causas","Supabase dice \"Invalid login credentials\" ante una contraseña errónea, un email desconocido y una cuenta de Google. Cómo saber cuál es.","2026-10-28","\u002Fblog\u002Fsupabase-invalid-login\u002Fcard-800x500.png",{"path":879,"title":880,"description":881,"published":882,"category":755,"image":883,"draft":759},"\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code","\"Edge Function returned a non-2xx status code\": el error real","\"Edge Function returned a non-2xx status code\" significa que tu función de Supabase respondió con un error. Aquí ves dónde leer el real.","2026-10-27","\u002Fblog\u002Fedge-function-returned-a-non-2xx-status-code\u002Fcard-800x500.png",{"path":885,"title":886,"description":887,"published":888,"category":755,"image":889,"draft":759},"\u002Fblog\u002Fcertificate-not-trusted","Certificado no confiable: tres causas aparte de la caducidad","¿Certificado no confiable aunque la fecha está bien? Nombra otra dirección, se firmó a sí mismo o a su cadena le falta un eslabón. Cómo saber cuál.","2026-10-26","\u002Fblog\u002Fcertificate-not-trusted\u002Fcard-800x500.png",{"path":789,"title":5,"description":758,"published":790,"category":755,"image":777,"draft":759},{"path":892,"title":893,"description":894,"published":895,"category":755,"image":896,"draft":759},"\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint","Supabase \"duplicate key value violates unique constraint\", explicado","Supabase \"duplicate key value violates unique constraint\": el valor ya está ocupado. Encuentra la columna, elige un arreglo y aparta el valor de la pantalla.","2026-10-24","\u002Fblog\u002Fduplicate-key-value-violates-unique-constraint\u002Fcard-800x500.png",{"path":898,"title":899,"description":900,"published":901,"category":755,"image":902,"draft":759},"\u002Fblog\u002Fsupabase-relation-does-not-exist","Supabase \"relation does not exist\" aunque la tabla está ahí","Supabase \"relation does not exist\" o PGRST205: ninguna tabla tiene ese nombre en ese esquema. Cuatro comprobaciones, y lo que abre exponer un esquema.","2026-10-23","\u002Fblog\u002Fsupabase-relation-does-not-exist\u002Fcard-800x500.png",{"path":392,"title":904,"description":905,"published":906,"category":755,"image":907,"draft":759},"Supabase PGRST202: \"Could not find the function in the schema cache\"","Supabase \"Could not find the function in the schema cache\" muestra los nombres de argumentos que envió tu app. Compáralos con la función.","2026-10-22","\u002Fblog\u002Fsupabase-function-not-found\u002Fcard-800x500.png",{"path":909,"title":910,"description":911,"published":912,"category":755,"image":913,"draft":759},"\u002Fblog\u002Fsupabase-schema-cache-column-error","Supabase PGRST204: \"Could not find the column in the schema cache\"","Supabase \"Could not find the column in the schema cache\" suele ser un nombre que no coincide. Cuatro comprobaciones y la línea que recarga.","2026-10-21","\u002Fblog\u002Fsupabase-schema-cache-column-error\u002Fcard-800x500.png",{"path":915,"title":916,"description":917,"published":918,"category":755,"image":919,"draft":759},"\u002Fblog\u002Fsupabase-email-rate-limit-exceeded","Supabase \"Email Rate Limit Exceeded\": dos límites, un solo código","Supabase \"Email Rate Limit Exceeded\" limita cada email que envía tu proyecto: 2 por hora con el remitente integrado. Qué cuenta y cómo subirlo.","2026-10-20","\u002Fblog\u002Fsupabase-email-rate-limit-exceeded\u002Fcard-800x500.png",{"path":921,"title":922,"description":923,"published":924,"category":755,"image":925,"draft":759},"\u002Fblog\u002Fsupabase-users-logged-out","Supabase \"Invalid Refresh Token: Refresh Token Not Found\": 3 causas","Supabase \"Invalid Refresh Token: Refresh Token Not Found\" cierra la sesión de tus usuarios. Tres causas, y cómo las separa el log de Auth.","2026-10-19","\u002Fblog\u002Fsupabase-users-logged-out\u002Fcard-800x500.png",{"path":927,"title":928,"description":929,"published":930,"category":755,"image":931,"draft":759},"\u002Fblog\u002Fpermission-denied-for-schema-public","Supabase \"permission denied for schema public\": tres causas","Supabase \"permission denied for schema public\" es un grant que falta en el esquema. Qué lo quitó y la línea que lo devuelve.","2026-10-18","\u002Fblog\u002Fpermission-denied-for-schema-public\u002Fcard-800x500.png",{"path":933,"title":934,"description":935,"published":936,"category":755,"image":937,"draft":759},"\u002Fblog\u002Fsupabase-bucket-not-found","Supabase \"Bucket not found\": las tres cosas que significa","Supabase \"Bucket not found\" es una de tres cosas: un enlace público a un bucket privado, un nombre que no coincide exactamente o el proyecto equivocado.","2026-10-17","\u002Fblog\u002Fsupabase-bucket-not-found\u002Fcard-800x500.png",{"path":939,"title":940,"description":941,"published":942,"category":755,"image":943,"draft":759},"\u002Fblog\u002Fsupabase-invalid-jwt","Supabase invalid JWT: ¿qué clave dejó de funcionar?","Un error Supabase invalid JWT es PGRST301: un token firmado no pasó la verificación. En un proyecto nuevo se trata de la sesión, no de la clave de API.","2026-10-16","\u002Fblog\u002Fsupabase-invalid-jwt\u002Fcard-800x500.png",{"path":598,"title":945,"description":946,"published":947,"category":755,"image":948,"draft":759},"Supabase \"multiple (or no) rows returned\" casi siempre es ninguna","Supabase devuelve PGRST116, \"JSON object requested, multiple (or no) rows returned\", cuando tu consulta no encontró nada. Una fila oculta parece ausente.","2026-10-15","\u002Fblog\u002Fsupabase-single-row-error\u002Fcard-800x500.png",{"path":950,"title":951,"description":952,"published":953,"category":755,"image":954,"draft":759},"\u002Fblog\u002Fdatabase-error-saving-new-user","\"Database error saving new user\" en Supabase: dónde mirar","\"Database error saving new user\" significa que algo unido a tu registro de Supabase falló y se deshizo entero. El error real está en tus logs.","2026-10-14","\u002Fblog\u002Fdatabase-error-saving-new-user\u002Fcard-800x500.png",{"path":956,"title":957,"description":958,"published":959,"category":755,"image":960,"draft":759},"\u002Fblog\u002Fwhat-is-supabase","¿Qué es Supabase? La base de datos que eligió tu builder","Qué es Supabase: la base de datos, el login y el almacenamiento de archivos de tu app. Supabase lleva los servidores; las reglas sobre tus filas son tuyas.","2026-10-13","\u002Fblog\u002Fwhat-is-supabase\u002Fcard-800x500.png",{"path":962,"title":963,"description":964,"published":965,"category":755,"image":966,"draft":759},"\u002Fblog\u002Faws-access-key-exposed-in-frontend","Una clave AWS expuesta en tu frontend no es como una clave OpenAI","Una clave de acceso de AWS expuesta en tu frontend lee y escribe tu almacenamiento. La solución suele ser una presigned URL, no una clave nueva.","2026-10-12","\u002Fblog\u002Faws-access-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":968,"title":969,"description":970,"published":971,"category":755,"image":972,"draft":759},"\u002Fblog\u002Fstorage-upload-violates-row-level-security","\"Row-level security policy for table objects\" al subir","\"New row violates row-level security policy for table objects\" significa que tu subida no tiene regla insert. Hacer público el bucket no añade ninguna.","2026-10-11","\u002Fblog\u002Fstorage-upload-violates-row-level-security\u002Fcard-800x500.png",{"path":974,"title":975,"description":976,"published":977,"category":755,"image":978,"draft":759},"\u002Fblog\u002Fbase44-security-scan","Escaneo de seguridad de Base44: lo que solo él ve","El escaneo de seguridad de Base44 revisa siete tipos de problema desde dentro. Esta es la mitad que lee y nadie más puede leer, y la mitad que nunca mira.","2026-10-10","\u002Fblog\u002Fbase44-security-scan\u002Fcard-800x500.png",{"path":980,"title":981,"description":982,"published":983,"category":755,"image":984,"draft":759},"\u002Fblog\u002Fapi-key-leaked-what-to-do","Tu clave API se filtró. El orden en el que hay que actuar","Una clave API se filtró y no sabes por dónde empezar. No todas las claves de tu frontend lo son, y el orden importa más que la velocidad.","2026-10-09","\u002Fblog\u002Fapi-key-leaked-what-to-do\u002Fcard-800x500.png",{"path":986,"title":987,"description":988,"published":989,"category":755,"image":990,"draft":759},"\u002Fblog\u002Finfinite-recursion-in-policy-for-relation","\"Infinite recursion detected in policy\" sin desactivar RLS","\"Infinite recursion detected in policy for relation\" significa que tu policy preguntó a la tabla que protege. Así se rompe el círculo.","2026-10-08","\u002Fblog\u002Finfinite-recursion-in-policy-for-relation\u002Fcard-800x500.png",{"path":992,"title":993,"description":994,"published":995,"category":755,"image":996,"draft":759},"\u002Fblog\u002Fno-api-key-found-in-request","\"No API key found in request\" en Supabase, y el arreglo equivocado","\"No API key found in request\" significa que tu petición llegó a Supabase sin clave. Casi todas las respuestas apuntan a las reglas de tu base de datos.","2026-10-07","\u002Fblog\u002Fno-api-key-found-in-request\u002Fcard-800x500.png",{"path":998,"title":999,"description":1000,"published":1001,"category":755,"image":1002,"draft":759},"\u002Fblog\u002Fis-base44-safe","¿Es seguro Base44? Lo que vimos en 5.442 apps Base44","¿Es seguro Base44? Pasamos nueve comprobaciones a 5.442 apps Base44 en producción. Casi todas sacaron B por motivos que su dueño nunca eligió.","2026-10-06","\u002Fblog\u002Fis-base44-safe\u002Fcard-800x500.png",{"path":1004,"title":1005,"description":1006,"published":1007,"category":755,"image":1008,"draft":759},"\u002Fblog\u002Fmove-a-secret-to-an-edge-function","Ocultar una clave API: muévela a una Edge Function de Supabase","Ocultar una clave API significa sacarla del navegador, y una Edge Function de Supabase es el sitio más pequeño donde ponerla. Dos pasos importan más.","2026-10-05","\u002Fblog\u002Fmove-a-secret-to-an-edge-function\u002Fcard-800x500.png",{"path":1010,"title":1011,"description":1012,"published":1013,"category":755,"image":1014,"draft":759},"\u002Fblog\u002Fenv-file-exposed-on-your-server","¿Tu archivo .env está expuesto? Las doce rutas a revisar","¿Tu archivo .env está expuesto en tu propio servidor web? Doce direcciones te lo dicen, y un acierto significa que todo lo que contiene ya es público.","2026-10-04","\u002Fblog\u002Fenv-file-exposed-on-your-server\u002Fcard-800x500.png",{"path":1016,"title":1017,"description":1018,"published":1019,"category":755,"image":1020,"draft":759},"\u002Fblog\u002Fis-v0-safe","Seguridad de v0: las 1.790 aplicaciones escaneadas sacaron una A","Seguridad de v0, medida en 1.790 aplicaciones v0 en vivo: todas sacaron A. Solo 17 nombraban una base de datos, y eso es casi todo lo que mide la A.","2026-10-03","\u002Fblog\u002Fis-v0-safe\u002Fcard-800x500.png",{"path":1022,"title":1023,"description":1024,"published":1025,"category":755,"image":1026,"draft":759},"\u002Fblog\u002Fis-bolt-safe","¿Es seguro Bolt? Lo que vimos en 1.123 aplicaciones Bolt","¿Es seguro Bolt? Pasamos 1.123 aplicaciones Bolt en vivo por nueve comprobaciones. El hosting salió limpio. Los hallazgos eran claves y tablas abiertas dentro.","2026-10-02","\u002Fblog\u002Fis-bolt-safe\u002Fcard-800x500.png",{"path":1028,"title":1029,"description":1030,"published":1031,"category":755,"image":1032,"draft":759},"\u002Fblog\u002Fis-supabase-down-or-your-app","¿Supabase está caído o es tu app? Cómo distinguirlo","¿Supabase está caído o es tu app? Un proyecto pausado, una base de datos llena y un fallo tuyo parecen lo mismo. Cuatro comprobaciones los separan.","2026-10-01","\u002Fblog\u002Fis-supabase-down-or-your-app\u002Fcard-800x500.png",{"path":1034,"title":1035,"description":1036,"published":1037,"category":755,"image":1038,"draft":759},"\u002Fblog\u002Fdoes-supabase-encrypt-my-data","¿Supabase cifra mis datos? Sí. Esto es lo que detiene","¿Supabase cifra los datos? Sí: AES-256 en reposo, TLS en tránsito, SOC 2 e ISO 27001. Qué cubre cada uno, y la fuga que ninguno de ellos detiene.","2026-09-30","\u002Fblog\u002Fdoes-supabase-encrypt-my-data\u002Fcard-800x500.png",{"path":1040,"title":1041,"description":1042,"published":1043,"category":755,"image":1044,"draft":759},"\u002Fblog\u002Fsupabase-free-plan-limits","¿Cuánto cuesta Supabase? Los límites del plan gratuito","Los límites del plan gratuito de Supabase: 500 MB de base de datos, 5 GB de egress y ningún backup. Qué pasa en cada uno y qué cambia Pro por $25.","2026-09-29","\u002Fblog\u002Fsupabase-free-plan-limits\u002Fcard-800x500.png",{"path":1046,"title":1047,"description":1048,"published":1049,"category":864,"image":1050,"draft":759},"\u002Fblog\u002Ftest-your-supabase-backup","Prueba tu backup de Supabase antes del día que lo necesites","Cómo probar tu backup de Supabase: restaurarlo en un proyecto de prueba, contar filas, iniciar sesión y buscar la línea que le falta a un archivo cortado.","2026-09-28","\u002Fblog\u002Ftest-your-supabase-backup\u002Fcard-800x500.png",{"path":1052,"title":1053,"description":1054,"published":1055,"category":864,"image":1056,"draft":759},"\u002Fblog\u002Fsupabase-backup-github-action","Backup gratis de Supabase con una GitHub Action, y la trampa","Un backup de Supabase con GitHub Action es gratis y sirve a muchas apps. El workflow, el connection string correcto y el egress de cada ejecución.","2026-09-27","\u002Fblog\u002Fsupabase-backup-github-action\u002Fcard-800x500.png",{"path":1058,"title":1059,"description":1060,"published":1061,"category":864,"image":1062,"draft":759},"\u002Fblog\u002Fdownload-your-supabase-backup","Por qué no puedes descargar tu backup de Supabase","No puedes descargar tu backup de Supabase en un proyecto actual: la copia diaria es una instantánea física. Cómo comprobarlo y tener tu propia copia.","2026-09-26","\u002Fblog\u002Fdownload-your-supabase-backup\u002Fcard-800x500.png",{"path":1064,"title":1065,"description":1066,"published":1061,"category":755,"image":1067,"draft":759},"\u002Fblog\u002Fsupabase-permission-denied-for-table","Supabase \"permission denied for table\": el grant que falta","Desde el 30 de octubre, una tabla nueva de Supabase responde \"permission denied for table\" hasta que le das acceso. El grant del email es la mitad del arreglo.","\u002Fblog\u002Fsupabase-permission-denied-for-table\u002Fcard-800x500.png",{"path":1069,"title":1070,"description":1071,"published":1072,"category":755,"image":1073,"draft":759},"\u002Fblog\u002Fopen-api-endpoint-exposed","¿Un endpoint de API abierto es un problema? Mira el JSON","Tu escaneo señaló un endpoint de API abierto. Que importe depende de lo que devolvió, y la mayoría de los que encontramos eran de la plataforma.","2026-09-25","\u002Fblog\u002Fopen-api-endpoint-exposed\u002Fcard-800x500.png",{"path":1075,"title":1076,"description":1077,"published":1078,"category":755,"image":1079,"draft":759},"\u002Fblog\u002Flovable-security-scan","Escaneo de seguridad de Lovable: lo que no puede probar","Escaneo de seguridad de Lovable: qué revisan el Quick scan y el Deep scan, cuándo corre cada uno, y lo único que ningún escaneo desde dentro puede probar.","2026-09-24","\u002Fblog\u002Flovable-security-scan\u002Fcard-800x500.png",{"path":1081,"title":1082,"description":1083,"published":1084,"category":864,"image":1085,"draft":759},"\u002Fblog\u002Fsupabase-backup-auth-users","Por qué tu dump de Supabase no tiene usuarios dentro","Ejecuta supabase db dump por sí solo y obtienes la forma de tu base de datos y ninguna de sus filas, sin el esquema auth donde viven tus usuarios.","2026-09-23","\u002Fblog\u002Fsupabase-backup-auth-users\u002Fcard-800x500.png",{"path":1087,"title":1088,"description":1089,"published":1090,"category":755,"image":1091,"draft":759},"\u002Fblog\u002Fdomain-and-certificate-expiry","Dominio caducado, web caída: qué pasa a partir de ahora","Tu dominio caducó y tu web está caída. Aquí tienes el reloj que acaba de arrancar, por qué el certificado es el menor problema, y cómo comprobar ambos.","2026-09-22","\u002Fblog\u002Fdomain-and-certificate-expiry\u002Fcard-800x500.png",{"path":1093,"title":1094,"description":1095,"published":1096,"category":755,"image":1097,"draft":759},"\u002Fblog\u002Fis-lovable-safe","¿Es seguro Lovable? Lo que vimos en 18.554 aplicaciones Lovable","¿Es seguro Lovable? Pasamos 18.554 aplicaciones Lovable por nueve comprobaciones. La plataforma fue la más limpia de cinco. Los hallazgos estaban dentro.","2026-09-21","\u002Fblog\u002Fis-lovable-safe\u002Fcard-800x500.png",{"path":1099,"title":1100,"description":1101,"published":1102,"category":755,"image":1103,"draft":759},"\u002Fblog\u002Fvibe-coded-app-security-checklist","La lista de seguridad del vibe coding, en nueve comprobaciones","Una lista de seguridad del vibe coding con nueve puntos, cada uno verificable en tu app desde fuera por cualquiera, y cada uno con una prueba de una línea.","2026-09-20","\u002Fblog\u002Fvibe-coded-app-security-checklist\u002Fcard-800x500.png",{"path":1105,"title":1106,"description":1107,"published":1108,"category":755,"image":1109,"draft":759},"\u002Fblog\u002Fstripe-secret-key-in-frontend","Una clave secreta de Stripe en tu frontend puede mover dinero","Una clave secreta de Stripe expuesta en tu frontend puede reembolsar, cobrar y leer cada ficha de cliente que tienes. Tu clave pk_live_ sí debe estar ahí.","2026-09-19","\u002Fblog\u002Fstripe-secret-key-in-frontend\u002Fcard-800x500.png",{"path":1111,"title":1112,"description":1113,"published":1114,"category":755,"image":1115,"draft":759},"\u002Fblog\u002Fvite-and-next-public-env-vars","Variables de entorno de Vite expuestas: el prefijo dice publica esto","Variables de entorno de Vite expuestas en tu app: hicieron lo que pedía el prefijo. VITE_ y NEXT_PUBLIC_ significan publica esto, y la IA nunca supo el coste.","2026-09-18","\u002Fblog\u002Fvite-and-next-public-env-vars\u002Fcard-800x500.png",{"path":1117,"title":1118,"description":1119,"published":1120,"category":755,"image":1121,"draft":759},"\u002Fblog\u002Fbase44-source-maps","Seguridad en Base44: qué marca un escaneo y qué te toca arreglar a ti","Seguridad en Base44 en 5.438 apps escaneadas: tres hallazgos en casi todas son de la plataforma, el source map es la insignia de Base44 y lo tuyo es poco.","2026-09-17","\u002Fblog\u002Fbase44-source-maps\u002Fcard-800x500.png",{"path":1123,"title":1124,"description":1125,"published":1126,"category":755,"image":1127,"draft":759},"\u002Fblog\u002Fis-cursor-ai-safe","¿Es seguro Cursor AI? El editor, el código y la app que publicaste","¿Es seguro Cursor AI? Tres preguntas en una búsqueda: qué guarda Cursor, en qué falla el código que escribe y si la app que publicaste está abierta.","2026-09-16","\u002Fblog\u002Fis-cursor-ai-safe\u002Fcard-800x500.png",{"path":1129,"title":1130,"description":1131,"published":1132,"category":755,"image":1133,"draft":759},"\u002Fblog\u002Fis-replit-safe","¿Es seguro Replit? Lo que vimos en 3.042 aplicaciones Replit en vivo","¿Es seguro Replit? Pasamos 3.042 aplicaciones Replit en vivo por nueve comprobaciones externas. Los hallazgos estaban en la aplicación publicada, no en el host.","2026-09-15","\u002Fblog\u002Fis-replit-safe\u002Fcard-800x500.png",{"path":1135,"title":1136,"description":1137,"published":1138,"category":864,"image":1139,"draft":759},"\u002Fblog\u002Fsupabase-storage-backup","Supabase storage backup: tu copia de la base no tiene archivos","Un Supabase storage backup es una tarea aparte. Las copias de la base guardan la lista de tus archivos y ninguno de ellos, así que cada upload queda roto.","2026-09-14","\u002Fblog\u002Fsupabase-storage-backup\u002Fcard-800x500.png",{"path":1141,"title":1142,"description":1143,"published":1144,"category":864,"image":1145,"draft":759},"\u002Fblog\u002Fsupabase-point-in-time-recovery","Point-in-time recovery de Supabase: precio y límites","El point-in-time recovery de Supabase rebobina tu base de datos a cualquier segundo de la última semana. Cuesta $100 al mes sobre Pro y solo cubre la base.","2026-09-13","\u002Fblog\u002Fsupabase-point-in-time-recovery\u002Fcard-800x500.png",{"path":1147,"title":1148,"description":1149,"published":1150,"category":864,"image":1151,"draft":759},"\u002Fblog\u002Fsupabase-project-paused-recover","¿Proyecto de Supabase pausado? Tus datos siguen ahí","Supabase pausó tu proyecto tras una semana sin actividad. No se ha borrado nada, Restaurar está junto al nombre y tienes un año antes de que eso cambie.","2026-09-12","\u002Fblog\u002Fsupabase-project-paused-recover\u002Fcard-800x500.png",{"path":1153,"title":1154,"description":1155,"published":1156,"category":755,"image":1157,"draft":759},"\u002Fblog\u002Fsafest-ai-app-builder","¿Qué builder de apps con IA es más seguro? 30.998 apps escaneadas","¿Qué builder de apps con IA es más seguro? Escaneamos 30.998 apps activas de Lovable, Base44, Replit, v0 y Bolt. El builder no es lo que decide tu nota.","2026-09-11","\u002Fblog\u002Fsafest-ai-app-builder\u002Fcard-800x500.png",{"path":603,"title":1159,"description":1160,"published":1161,"category":755,"image":1162,"draft":759},"Activa Row Level Security en cada tabla de Supabase y compruébalo","Activar Row Level Security en Supabase sin política cierra la tabla del todo. Una política sin el interruptor no hace nada. Aquí tienes el SQL y la prueba.","2026-09-10","\u002Fblog\u002Fenable-rls-on-every-supabase-table\u002Fcard-800x500.png",{"path":1164,"title":1165,"description":1166,"published":1167,"category":755,"image":1168,"draft":759},"\u002Fblog\u002Fsupabase-rls-disabled-in-public","Supabase \"RLS disabled in public\": lo que el aviso no ve","Supabase marca \"RLS disabled in public\" como error. No dice nada de la política de lectura que deja tu tabla igual de abierta a cualquiera.","2026-09-09","\u002Fblog\u002Fsupabase-rls-disabled-in-public\u002Fcard-800x500.png",{"path":1170,"title":1171,"description":1172,"published":1173,"category":755,"image":1174,"draft":759},"\u002Fblog\u002Frotate-supabase-service-role-key","Cómo rotar una clave service_role de Supabase filtrada","Supabase dice que primero cierres la fuga. Otras guías dicen que rotes ya. Cuál es lo correcto depende de dónde se filtró tu clave service_role.","2026-09-08","\u002Fblog\u002Frotate-supabase-service-role-key\u002Fcard-800x500.png",{"path":1176,"title":1177,"description":1178,"published":1179,"category":755,"image":1180,"draft":759},"\u002Fblog\u002Fvibe-coding-security-scanners-compared","Escáneres de seguridad vibe coding comparados, el nuestro incluido","El mejor escáner de seguridad para vibe coding depende de tres preguntas que ninguna lista de funciones responde. Ocho herramientas, con precios.","2026-09-07","\u002Fblog\u002Fvibe-coding-security-scanners-compared\u002Fcard-800x500.png",{"path":1182,"title":1183,"description":1184,"published":1185,"category":755,"image":1186,"draft":759},"\u002Fblog\u002Fsupabase-security-checker","Supabase security checker: haz tú mismo las cinco comprobaciones","Un Supabase security checker lee tu app publicada en lugar de la configuración de tu proyecto. Estas son las cinco comprobaciones y cómo hacer cada una.","2026-09-06","\u002Fblog\u002Fsupabase-security-checker\u002Fcard-800x500.png",{"path":1188,"title":1189,"description":1190,"published":1191,"category":755,"image":1192,"draft":759},"\u002Fblog\u002Fvibe-coding-security-scanner","Escáner de seguridad para vibe coding: qué se le escapa","Un escáner de seguridad para vibe coding lee tu app en vivo desde fuera. Qué cubre eso, las cuatro cosas que no puede ver y cómo leer el resultado.","2026-09-05","\u002Fblog\u002Fvibe-coding-security-scanner\u002Fcard-800x500.png",{"path":1194,"title":1195,"description":1196,"published":1197,"category":755,"image":1198,"draft":759},"\u002Fblog\u002Fmissing-security-headers","Faltan cabeceras de seguridad: cuándo importa de verdad","Que falten cabeceras de seguridad es el hallazgo que más imprime nuestro escáner. Contra qué protegen y cuándo son la línea menos urgente de tu informe.","2026-09-04","\u002Fblog\u002Fmissing-security-headers\u002Fcard-800x500.png",{"path":1200,"title":1201,"description":1202,"published":1203,"category":755,"image":1204,"draft":759},"\u002Fblog\u002Fopenai-api-key-exposed-in-frontend","Tu clave de API de OpenAI está expuesta en tu frontend. Rótala.","Una clave de API de OpenAI expuesta en tu frontend no se puede atar a un dominio. Rótala hoy, mueve la llamada a tu servidor y limita el gasto.","2026-09-03","\u002Fblog\u002Fopenai-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1206,"title":1207,"description":1208,"published":1209,"category":755,"image":1210,"draft":759},"\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps","Clave de API expuesta en el frontend: qué llevan 30.998 apps","Una clave de API expuesta en tu frontend suele ser una clave de Google Maps. Escaneamos 30.998 apps vibe-coded y contamos qué secretos se filtran de verdad.","2026-09-02","\u002Fblog\u002Fwhat-secrets-leak-from-vibe-coded-apps\u002Fcard-800x500.png",{"path":1212,"title":1213,"description":1214,"published":1215,"category":864,"image":1216,"draft":759},"\u002Fblog\u002Fsupabase-backup-tools-compared","Herramientas de backup de Supabase comparadas, la nuestra incluida","Cuatro tipos de herramienta de backup de Supabase, qué copia cada una y cuándo una GitHub Action gratuita le gana a pagar a cualquiera, nosotros incluidos.","2026-09-01","\u002Fblog\u002Fsupabase-backup-tools-compared\u002Fcard-800x500.png",{"path":1218,"title":1219,"description":1220,"published":1221,"category":755,"image":1222,"draft":759},"\u002Fblog\u002Freplit-secrets-explained","Cómo usar secrets en Replit, y qué se publica de todos modos","Cómo usar secrets en Replit: añadir uno, leerlo y arreglar las dos causas del undefined. Y las claves que la herramienta Secrets no puede mantener privadas.","2026-08-31","\u002Fblog\u002Freplit-secrets-explained\u002Fcard-800x500.png",{"path":1224,"title":1225,"description":1226,"published":1227,"category":864,"image":1228,"draft":759},"\u002Fblog\u002Fback-up-supabase-free-tier","Backups del plan gratuito de Supabase: cómo hacer uno sin terminal","No hay backups en el plan gratuito de Supabase: la copia tienes que hacerla tú. Cómo sacarla desde el panel y qué deja fuera la vía CSV.","2026-08-30","\u002Fblog\u002Fback-up-supabase-free-tier\u002Fcard-800x500.png",{"path":1230,"title":1231,"description":1232,"published":1233,"category":755,"image":1234,"draft":759},"\u002Fblog\u002Fis-supabase-secure","¿Supabase es seguro? Sí. Tu proyecto es otra pregunta","¿Supabase es seguro? La plataforma está auditada, cifrada y sometida a pentests. Su propia documentación dice dónde termina eso y empiezan tus ajustes.","2026-08-29","\u002Fblog\u002Fis-supabase-secure\u002Fcard-800x500.png",{"path":1236,"title":1237,"description":1238,"published":1239,"category":755,"image":1240,"draft":759},"\u002Fblog\u002Fwhere-to-find-supabase-api-keys","Dónde están tus claves API de Supabase: anon, service_role y URL","La URL del proyecto, la clave anon y la clave service_role están en una sola página del panel. Aquí tienes dónde está, y cuál de los cuatro va en tu app.","2026-08-28","\u002Fblog\u002Fwhere-to-find-supabase-api-keys\u002Fcard-800x500.png",{"path":1242,"title":1243,"description":1244,"published":1245,"category":755,"image":1246,"draft":759},"\u002Fblog\u002Fnew-row-violates-row-level-security-policy","New row violates row-level security policy en Supabase. ¿Y ahora?","\"New row violates row-level security policy\" significa que Supabase rechazó una escritura. El arreglo rápido vuelve a abrir la tabla para cualquiera.","2026-08-27","\u002Fblog\u002Fnew-row-violates-row-level-security-policy\u002Fcard-800x500.png",{"path":1248,"title":1249,"description":1250,"published":1251,"category":755,"image":1252,"draft":759},"\u002Fblog\u002Fcors-wildcard-security-risk","¿Es un comodín CORS un riesgo de seguridad? Casi nunca.","¿Es un comodín CORS un riesgo de seguridad? Casi siempre es el ajuste por defecto de tu builder y no entrega nada que tu servidor no diera ya a cualquiera.","2026-08-26","\u002Fblog\u002Fcors-wildcard-security-risk\u002Fcard-800x500.png",{"path":1254,"title":1255,"description":1256,"published":1257,"category":864,"image":1258,"draft":759},"\u002Fblog\u002Fsupabase-branching-is-not-a-backup","Supabase branching no es un backup. Solo va hacia adelante.","Por qué el branching de Supabase no es un backup: una rama arranca sin tus datos y un merge solo mueve el esquema. Para qué sirve y qué usar en su lugar.","2026-08-25","\u002Fblog\u002Fsupabase-branching-is-not-a-backup\u002Fcard-800x500.png",{"path":1260,"title":1261,"description":1262,"published":1263,"category":864,"image":1264,"draft":759},"\u002Fblog\u002Fhow-to-restore-a-supabase-backup","Cómo restaurar un backup de Supabase, y qué se rompe después","Cómo restaurar un backup de Supabase desde el panel o desde un archivo de volcado, qué reemplaza la restauración y por qué tu app puede seguir rota al terminar.","2026-08-24","\u002Fblog\u002Fhow-to-restore-a-supabase-backup\u002Fcard-800x500.png",{"path":1266,"title":1267,"description":1268,"published":1269,"category":755,"image":1270,"draft":759},"\u002Fblog\u002Fsupabase-storage-bucket-public","Tu bucket de Supabase Storage es público. ¿Es un problema?","Un bucket público de Supabase Storage significa que quien tenga la URL de un archivo puede abrirlo. No significa que alguien pueda listar lo que hay dentro.","2026-08-23","\u002Fblog\u002Fsupabase-storage-bucket-public\u002Fcard-800x500.png",{"path":1272,"title":1273,"description":1274,"published":1275,"category":755,"image":1276,"draft":759},"\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend","¿Es un problema una clave de API de Google en tu frontend?","Una clave de API de Google en tu frontend es la clave que más encuentra nuestro escáner, y casi siempre no pasa nada. Un ajuste gratuito decide cuál es cuál.","2026-08-22","\u002Fblog\u002Fgoogle-api-key-exposed-in-frontend\u002Fcard-800x500.png",{"path":1278,"title":1279,"description":1280,"published":1281,"category":864,"image":1282,"draft":759},"\u002Fblog\u002Fai-agent-deleted-my-database","Un agente de IA borró mis datos de Supabase. ¿Qué puedo recuperar?","Un agente de IA borró datos de tu base. Lo que puedes recuperar ya estaba decidido antes de que se ejecutara, y los próximos minutos deciden el resto.","2026-08-21","\u002Fblog\u002Fai-agent-deleted-my-database\u002Fcard-800x500.png",{"path":1284,"title":1285,"description":1286,"published":1287,"category":755,"image":1288,"draft":759},"\u002Fblog\u002Fcan-anyone-read-your-supabase-database","¿Puede cualquiera leer tu base de datos Supabase? 3.680 apps","¿Puede cualquiera leer tu base de datos Supabase sin iniciar sesión? Escaneamos 30.998 apps activas hechas con builders de IA y medimos cuántas dicen que sí.","2026-08-18","\u002Fblog\u002Fcan-anyone-read-your-supabase-database\u002Fcard-800x500.png",{"path":1290,"title":1291,"description":1292,"published":1287,"category":755,"image":1293,"draft":759},"\u002Fblog\u002Fsource-maps-exposed-in-production","Source maps expuestos: tu app está publicando su código original","Un source map expuesto deja que cualquiera lea el código original de tu app, comentarios incluidos. La comprobación de 30 segundos y lo que de verdad importa.","\u002Fblog\u002Fsource-maps-exposed-in-production\u002Fcard-800x500.png",{"path":1295,"title":1296,"description":1297,"published":1298,"category":755,"image":1299,"draft":759},"\u002Fblog\u002Fsupabase-new-api-keys","Supabase publishable key: ¿es la misma que la anon key?","Supabase sustituyó anon y service_role por claves publicables y secretas. ¿Cuál pertenece a tu app y cuál no debe estar nunca?","2026-08-12","\u002Fblog\u002Fsupabase-new-api-keys\u002Fcard-800x500.png",{"path":1301,"title":1302,"description":1303,"published":1304,"category":864,"image":1305,"draft":759},"\u002Fblog\u002Fdoes-supabase-back-up-my-database","¿Supabase respalda mi base de datos? Depende de tu plan.","¿Supabase respalda tu base de datos? A diario en los planes de pago, y nada en el gratuito. Cómo saber cuál tienes y qué es lo que esa copia no cubre.","2026-08-11","\u002Fblog\u002Fdoes-supabase-back-up-my-database\u002Fcard-800x500.png",{"path":1307,"title":1308,"description":1309,"published":1310,"category":755,"image":1311,"draft":759},"\u002Fblog\u002Fsupabase-rls-on-but-table-still-public","Supabase Row Level Security está activado. Tu tabla sigue pública.","Activar Supabase Row Level Security no protege una tabla. Lo hacen tus políticas, y la que arregló tu app rota puede dejar entrar a cualquiera.","2026-08-10","\u002Fblog\u002Fsupabase-rls-on-but-table-still-public\u002Fcard-800x500.png",{"path":1313,"title":1314,"description":1315,"published":1310,"category":864,"image":1316,"draft":759},"\u002Fblog\u002Fversion-history-is-not-a-backup","El historial de versiones no es un backup. No recupera una tabla.","Lovable y Bolt guardan un historial de versiones de tu código. Tu base de datos es un servicio aparte, y volver atrás no te devuelve tus datos.","\u002Fblog\u002Fversion-history-is-not-a-backup\u002Fcard-800x500.png",{"path":1318,"title":1319,"description":1320,"published":1321,"category":864,"image":1322,"draft":759},"\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database","Tres formas de respaldar una base Supabase, y qué falta en cada una","El panel, pg_dump y un servicio gestionado. Qué guarda de verdad cada uno, qué deja fuera sin avisar y cuál sobrevive a perder la cuenta.","2026-08-09","\u002Fblog\u002Fthree-ways-to-back-up-a-supabase-database\u002Fcard-800x500.png",{"path":1324,"title":1325,"description":1326,"published":1321,"category":755,"image":1327,"draft":759},"\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend","Qué claves de API son seguras en el navegador y cuáles no","Tu clave anon de Supabase está pensada para ser pública. Tu clave service_role no, y se salta todas las reglas que pongas. Cómo distinguirlas.","\u002Fblog\u002Fwhich-api-keys-are-safe-in-your-frontend\u002Fcard-800x500.png",1794213645099]