Laravel: fixing the “Unknown column ‘updated_at’” error

Introduction The Eloquent ORM that ships with Laravel gives you an easy way to interact with your database. It simplifies every CRUD operation (create, read, update and delete) and any other database query. As described in the official Laravel documentation, Eloquent expects created_at and updated_at to exist

Laravel: fixing the “Unknown column ‘updated_at’” error
Quick answer

Eloquent writes created_at and updated_at on every insert: the error means the table does not have them. Three fixes, depending on the case: add them with a migration using $table->timestamps(), turn them off with public $timestamps = false; on the model, or declare their real names with the CREATED_AT and UPDATED_AT constants.

The message shows up on the first save() or create() on a model:

code
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'updated_at' in 'INSERT INTO'

The cause is always the same: Eloquent maintains two date columns, created_at and updated_at, and writes them on every insert or update. If the table does not have them, the query fails. There are three ways to fix this, and the right one depends on the table, not on your preferences.

Part of the Web development reading path. Error reproduced and fixes verified on Laravel 13.30.1 with MariaDB 11.8.9.

Recognising the error

The wording varies with the engine and the operation, but it always points at updated_at or created_at:

code
-- à 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_at

Start by checking what the table actually contains:

bash
php artisan tinker --execute='echo implode(", ", Schema::getColumnListing("reports"));'
code
id, title

If created_at and updated_at are missing, read on. If they are there under another name, go straight to the third solution.

Solution 1: add the columns

The full procedure is described in the guide to Laravel migrations.

This is the right answer in most cases. Knowing when a row was created and last changed always pays off sooner or later, for debugging as much as for sorting. The timestamps() helper creates both columns at once, nullable:

bash
php artisan make:migration add_timestamps_to_reports_table --table=reports
database/migrations/2026_09_02_000000_add_timestamps_to_reports_table.php
public function up(): void
{
    Schema::table('reports', function (Blueprint $table) {
        $table->timestamps();
    });
}

public function down(): void
{
    Schema::table('reports', function (Blueprint $table) {
        $table->dropTimestamps();
    });
}
bash
php artisan migrate

After the migration, the insert goes through and the dates are filled in. Verified: the table does hold id, title, created_at, updated_at and $report->created_at returns an Illuminate\Support\Carbon instance, which you can format and compare.

On a table that already holds rows, the columns are created as NULL on the existing data, which causes no trouble at all. If you want a value there, set it explicitly:

php
$table->timestamp('created_at')->nullable()->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrent()->useCurrentOnUpdate();

Solution 2: turn timestamps off on the model

When the table is not meant to carry these dates, a pivot table, a frozen reference table or a schema imposed by a third-party system, say so on the model:

app/Models/Video.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Video extends Model
{
    public $timestamps = false;
}

Eloquent then stops writing the two columns, and the insert works. You can confirm the state with $video->usesTimestamps(), which returns false.

The same switch can be flipped on a single instance, for example to correct a value without changing updated_at:

php
$task->timestamps = false;
$task->title = 'Titre corrigé';
$task->save();   // updated_at stays unchanged
Do not turn them off just to silence the error

If the table ought to have these columns, $timestamps = false fixes nothing: it removes information you will go looking for later. Keep this solution for tables where a modification date genuinely makes no sense.

Solution 3: the columns exist under another name

On a legacy database, the columns are often called date_creation, creation_date or modified_on. There is no need to rename anything: give Eloquent the real names with two constants.

app/Models/Clip.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Clip extends Model
{
    const CREATED_AT = 'creation_date';
    const UPDATED_AT = 'updated_date';
}

Eloquent now writes to those columns. Verified on Laravel 13: after Clip::create(['title' => 'Extrait']), the creation_date column does hold the timestamp, and getUpdatedAtColumn() returns updated_date.

If only one of the two dates exists, set the other one to null:

php
const CREATED_AT = 'creation_date';
const UPDATED_AT = null;   // no update column

The $clip->created_at and $clip->updated_at accessors, however, do not follow these constants: they return null. Read the column by its real name, $clip->creation_date, or generically with $clip->{$clip->getCreatedAtColumn()}.

Which solution to pick

Situation Solution
Ordinary application table, columns simply forgotten Add the columns with a migration
Pivot table, frozen reference table, imposed schema public $timestamps = false;
Legacy database with date columns named differently CREATED_AT and UPDATED_AT constants
One write must not touch updated_at $model->timestamps = false; before save()

Common errors

Turning timestamps off to silence the error On an ordinary application table, $timestamps = false removes useful information instead of fixing the schema. Add the columns instead.
NOT NULL columns on a table that already holds rows timestamps() creates them nullable, which works. A NOT NULL definition with no default value would make the migration fail.
Renaming the columns of the legacy database Pointless and risky: the CREATED_AT and UPDATED_AT constants are enough to point at the existing names.
Forgetting UPDATED_AT = null If the table only has a creation column, leaving UPDATED_AT at its default brings the same error back on update.

EloquentLaravelMigrationsPHP

Damien Flandrin Web developer since 2010, creator of Gekkode and Email Impact. Every article is tested on a real project before publication. Contact
Newsletter

New tests, tutorials and projects, by e-mail.

Reproducible tests, versioned code, dated results. Never any spam.