Ir al contenido

Solución de problemas

Lo que tienes es un síntoma: un mensaje de error o algo que no pasa. Así está ordenada esta página. Busca el tuyo, y debajo tienes la causa y el arreglo.

Casi todos salen de la configuración, no del código, y casi todos se arreglan con una línea.

«No migrations to apply!» y las migraciones están ahí

Sección titulada ««No migrations to apply!» y las migraciones están ahí»

Síntoma. wrangler d1 migrations apply —o cms db:apply, que lo llama por dentro— dice que no hay nada que aplicar, con el directorio migrations/ lleno de carpetas delante.

Causa. Falta migrations_pattern en tu wrangler.jsonc. drizzle-kit 1.x escribe migrations/<fecha>_<nombre>/migration.sql, un nivel más hondo que el migrations/*.sql que wrangler busca por defecto. Wrangler mira donde le dijeron y no encuentra nada, que es la verdad desde su punto de vista.

Arreglo. Una línea en el bloque de D1:

wrangler.jsonc
{
"d1_databases": [
{
"binding": "DB",
"database_name": "mi-sitio",
"database_id": "<id>",
"migrations_dir": "migrations",
"migrations_pattern": "migrations/*/migration.sql"
}
]
}

Ver Instalación.

Síntoma. Este mensaje, que es de la CLI y no de wrangler:

No hay ninguna migración que aplicar. Ejecuta `cms db:migrate` para generarla.

Causa. Distinta de la anterior: aquí el directorio migrations/ está vacío de verdad. Has lanzado db:apply antes de generar nada.

Arreglo. Son dos comandos, siempre en este orden: db:migrate escribe el SQL, tú lo lees, db:apply lo aplica.

Ventana de terminal
pnpm cms db:migrate
pnpm cms db:apply

Encadenarlos en un solo comando es justo lo que la CLI evita a propósito. Ver CLI.

db:migrate falla diciendo que drizzle-kit no está

Sección titulada «db:migrate falla diciendo que drizzle-kit no está»

Síntoma.

drizzle-kit no está instalado y `cms db:migrate` lo necesita para generar la migración.
Instálalo con `pnpm add -D drizzle-kit` (o el equivalente de tu gestor).

Causa. drizzle-kit es quien escribe el SQL, y no viaja dentro del paquete: lo lanzamos como subproceso para no arrastrarlo al node_modules de producción de todo el que instale el CMS.

Arreglo. Instálalo como dependencia de desarrollo, y añade su config si aún no la tienes:

Ventana de terminal
pnpm add -D drizzle-kit
drizzle.config.ts
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
dialect: 'sqlite',
schema: './.cms/schema.ts',
out: './migrations',
})

Ver Schema y migraciones.

env.KV es undefined y el binding estaba declarado

Sección titulada «env.KV es undefined y el binding estaba declarado»

Síntoma. El sitio arranca, pero todo lo que vive en KV —los ajustes del sitio— falla. Tu wrangler.jsonc declara KV y aun así no está.

Causa. Falta SESSION en kv_namespaces. Ese namespace no es del CMS: es de @astrojs/cloudflare, que guarda ahí las sesiones de Astro. Cuando no lo encuentra, el adaptador sustituye el array entero por el suyo, y KV desaparece sin un solo aviso.

Arreglo. Declara los dos, aunque tú solo uses uno:

wrangler.jsonc
{
"kv_namespaces": [
{ "binding": "SESSION", "id": "<id>" },
{ "binding": "KV", "id": "<id>" }
]
}

Ver Instalación.

Síntoma. El sitio arranca y el contenido se lee, pero cualquier ruta de autenticación revienta con un error de módulo de Node.

Causa. Falta la marca nodejs_compat. La autenticación es better-auth, y better-auth usa node:crypto, que en el runtime de Workers no existe sin esa marca.

Arreglo.

wrangler.jsonc
{ "compatibility_flags": ["nodejs_compat"] }

Ver Autenticación.

MISSING_EMAIL en la primera lectura de sesión

Sección titulada «MISSING_EMAIL en la primera lectura de sesión»

Síntoma. El contenido y la API REST funcionan, pero en cuanto algo lee la sesión salta MISSING_EMAIL. Nadie puede autenticarse.

Causa. El correo no está configurado, y la verificación de la dirección es obligatoria. El error es perezoso —no tumba el arranque— y aparece cuando de verdad hace falta mandar un correo. Alguno de estos falta: el binding send_email, un email.from no vacío, o los tres requisitos de plataforma: plan Workers de pago, dominio con DNS de Cloudflare dado de alta en Email Sending, y que from pertenezca a ese dominio.

Arreglo. El propio mensaje enumera los tres requisitos, porque saber solo que «falta el binding» manda a mirar al sitio equivocado dos de cada tres veces. En local basta con el binding y un from cualquiera que no sea vacío:

wrangler.jsonc
{ "send_email": [{ "name": "EMAIL" }] }
cms.config.ts
import { defineConfig } from '@kevolution-co/cms'
export default defineConfig({
email: { from: 'noreply@tudominio.com', siteName: 'Mi sitio' },
collections: [],
})

Ver Email.

Síntoma. El flujo de verificación o de restablecimiento se completa sin errores, pero al buzón no llega nada.

Causa. En local es lo esperado, no un fallo: sin "remote": true, wrangler dev simula el envío. Escribe el mensaje en un fichero temporal e imprime su ruta en la consola.

[wrangler:info] send_email binding called with MessageBuilder:
From: noreply@example.com
To: kevin@example.com
Subject: Verifica tu cuenta en Kevin CMS
Text: /tmp/miniflare-…/email-text/<id>.txt

Arreglo. Abre ese fichero y sigue el enlace: los flujos completos se desarrollan así, sin dominio ni plan de pago. Si lo que quieres es mandar correo de verdad desde tu máquina, añade "remote": true al binding —y cumple antes los tres requisitos de plataforma—:

wrangler.jsonc
{ "send_email": [{ "name": "EMAIL", "remote": true }] }

Desplegado no hay simulación que valga: ahí el envío es real siempre. Ver Email y Despliegue.

Síntoma.

{
"error": "UNAUTHORIZED",
"message": "Esta operación sobre \"posts\" requiere una sesión iniciada"
}

Causa. Leer por la API REST exige sesión salvo que la colección diga lo contrario. Sin access: { read: 'public' }, un GET sin cookie es 401.

Arreglo. Si esa colección se lee sin sesión —un blog, un catálogo—, decláralo:

cms.config.ts
defineCollection({
slug: 'posts',
access: { read: 'public' },
fields: [{ name: 'title', type: 'text', required: true }],
})

'public' es el único valor admitido; cualquier otro es un error de configuración al arrancar. Y esto solo abre las lecturas: escribir sigue exigiendo sesión siempre.

Ver API REST.

Astro.locals.cms.collections.posts no está tipado

Sección titulada «Astro.locals.cms.collections.posts no está tipado»

Síntoma. El editor no autocompleta tus colecciones, o posts sale como error de tipo, aunque .cms/types.d.ts exista con todo dentro.

Causa. El fichero no está incluido en tu tsconfig.json. Un comodín no entra en un directorio cuyo nombre empieza por punto, así que **/* no lo alcanza: hay que nombrarlo.

Arreglo.

tsconfig.json
{
"include": [
".astro/types.d.ts",
"./.cms/types.d.ts",
"**/*"
]
}

La integración lo comprueba al arrancar y lo dice, pero como aviso y no como error: sin esa línea todo funciona, solo pierdes el tipado. Ver Integración de Astro.