
Eloquent escribe created_at y updated_at en cada inserción: el error significa que la tabla no las tiene. Tres soluciones según el caso: añádelas con una migración usando $table->timestamps(), desactívalas con public $timestamps = false; en el modelo, o declara sus nombres reales con las constantes CREATED_AT y UPDATED_AT.
El mensaje salta en el primer save() o create() sobre un modelo:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'La causa es siempre la misma: Eloquent mantiene dos columnas de fecha, created_at y updated_at, y las escribe en cada inserción o actualización. Si la tabla no las tiene, la consulta falla. Hay tres formas de arreglarlo, y la buena depende de la tabla, no de tus preferencias.
Artículo del itinerario Desarrollo web. Error reproducido y soluciones verificadas en Laravel 13.30.1 con MariaDB 11.8.9.
Reconocer el error
El texto cambia según el motor y la operación, pero siempre señala a updated_at o a created_at:
-- à l'insertion (MySQL / MariaDB)
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'
-- à la mise à jour
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'reports.updated_at' in 'SET'
-- sur SQLite
SQLSTATE[HY000]: General error: 1 table reports has no column named updated_atComprueba primero qué contiene realmente la tabla:
php artisan tinker --execute='echo implode(", ", Schema::getColumnListing("reports"));'id, titleSi faltan created_at y updated_at, sigue leyendo. Si están pero con otro nombre, ve directamente a la tercera solución.
Solución 1: añadir las columnas
El procedimiento completo está descrito en la guía de migraciones de Laravel.
Es la respuesta correcta en la mayoría de los casos. Saber cuándo se creó y cuándo se modificó una fila acaba sirviendo, tanto para depurar como para ordenar. El helper timestamps() crea las dos columnas de una vez, nullables:
php artisan make:migration add_timestamps_to_reports_table --table=reportspublic function up(): void
{
Schema::table('reports', function (Blueprint $table) {
$table->timestamps();
});
}
public function down(): void
{
Schema::table('reports', function (Blueprint $table) {
$table->dropTimestamps();
});
}php artisan migrateDespués de la migración, la inserción pasa y las fechas se rellenan. Verificado: la tabla contiene id, title, created_at, updated_at y $report->created_at devuelve una instancia de Illuminate\Support\Carbon, que puedes formatear y comparar.
En una tabla que ya tiene filas, las columnas se crean a NULL sobre lo existente, y eso no da ningún problema. Si prefieres un valor, decláralo explícitamente:
$table->timestamp('created_at')->nullable()->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate();Solución 2: desactivar los timestamps en el modelo
Cuando la tabla no tiene por qué llevar esas fechas (tabla de enlace, catálogo fijo o esquema impuesto por un sistema externo), díselo al modelo:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
public $timestamps = false;
}Eloquent deja de rellenar las dos columnas y la inserción funciona. Puedes confirmarlo con $video->usesTimestamps(), que devuelve false.
La misma desactivación se puede hacer puntualmente sobre una instancia, por ejemplo para corregir un dato sin tocar updated_at:
$task->timestamps = false;
$task->title = 'Titre corrigé';
$task->save(); // updated_at no cambiaSi la tabla debería tener esas columnas, $timestamps = false no arregla nada: quita una información que buscarás más adelante. Reserva esta solución para las tablas donde la fecha de modificación realmente no tiene sentido.
Solución 3: las columnas existen con otro nombre
En una base heredada, las columnas suelen llamarse date_creation, creation_date o modified_on. No hace falta renombrar nada: indícale a Eloquent los nombres reales con dos constantes.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Clip extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}A partir de ahí Eloquent escribe en esas columnas. Verificado en Laravel 13: después de Clip::create(['title' => 'Extrait']), la columna creation_date contiene la marca de tiempo y getUpdatedAtColumn() devuelve updated_date.
Si solo existe una de las dos fechas, pon la otra a null:
const CREATED_AT = 'creation_date';
const UPDATED_AT = null; // sin columna de actualizaciónLos accesores $clip->created_at y $clip->updated_at, en cambio, no siguen estas constantes: devuelven null. Lea la columna por su nombre real, $clip->creation_date, o de forma genérica con $clip->{$clip->getCreatedAtColumn()}.
Qué solución elegir
| Situación | Solución |
|---|---|
| Tabla de aplicación normal, columnas simplemente olvidadas | Añadir las columnas con una migración |
| Tabla pivote, catálogo fijo, esquema impuesto | public $timestamps = false; |
| Base heredada con columnas de fecha con otro nombre | Constantes CREATED_AT y UPDATED_AT |
Una escritura concreta no debe tocar updated_at | $model->timestamps = false; antes de save() |
Errores frecuentes
$timestamps = false elimina una información útil en vez de corregir el esquema. Añade las columnas.timestamps() las crea nullables, y así pasa. Una definición NOT NULL sin valor por defecto haría fallar la migración.

