Skip to content

Laravel error

Laravel: SQLSTATE[42S02] Base table or view not found

The error

SQLSTATE[42S02]: Base table or view not found: 1146 Table 'shop.orders' doesn't exist

The query is valid and the table is not there. Where that happens tells you which of the five causes you have, and only one of them is a missing migration.

The error

Illuminate\Database\QueryException

SQLSTATE[42S02]: Base table or view not found: 1146
Table 'shop.orders' doesn't exist

Postgres words it differently and means the same thing:

SQLSTATE[42P01]: Undefined table: 7 ERROR: relation "orders" does not exist

Read the name before anything else

The message contains the database and the table the query asked for. Both are useful and both get skipped.

SHOW TABLES;                          -- MySQL
\dt                                   -- Postgres

Comparing that list with the name in the error resolves most of these in under a minute, because the answer is usually either that the table has a different name than you expected or that you are connected to a different database than you expected.

The five causes

The migration has not run. The obvious one, and the least interesting.

php artisan migrate --force

Worth checking what the framework thinks has run, which is not the same as what is in your migrations folder:

php artisan migrate:status

It ran somewhere else. The most common cause on a real system. A migration executed against staging, or against the local database, while the application is reading production. Or the web process uses one .env and a queued worker was started with another. The database name in the error is the evidence - compare it with the one in your connection config.

The model's table name is not the one you assumed. Eloquent derives the table from the class name and pluralises it in English. Person becomes people, Status becomes statuses, and a class named after a Turkish or German business term becomes something nobody intended.

class OrderLine extends Model
{
    protected $table = 'order_lines';   // say it rather than infer it
}

The test database has no schema. A test that hits the database needs one, and the trait that builds it has to be on the test class:

uses(RefreshDatabase::class);

Without it the suite runs against whatever the test connection points at, which on a fresh checkout or a CI runner is an empty database.

A deploy is halfway through. New code deployed before its migration ran, or a worker still running old code after a table was renamed. Queries against a table that exists in one version and not the other fail for as long as the two versions overlap.

The one that only happens in production

Table names are case sensitive on Linux and usually not on macOS. A model referring to Orders while the table is orders works perfectly on a developer's machine and fails on every server.

If the error appears only after deployment and the name looks right, compare the case character by character.

Renaming a table without an outage

The reason this error shows up during otherwise careful deploys is that a rename is two states with a gap between them, and requests arrive in the gap.

Do it in steps instead:

  1. Create the new table alongside the old one.
  2. Write to both, read from the old.
  3. Backfill, then switch reads to the new one.
  4. Stop writing the old one.
  5. Drop it in a later release.

Slower, and nothing fails while it happens - which is the trade every schema change on a live system is making, whether or not anybody decided it deliberately.

One step further along the same path is a foreign key that cannot find its parent. By then the table exists and the rows it points at do not.

The five-step shape above is what a locking schema change needs as well. Which of your migrations need it, and which are safe in one step, depends on your row counts, and we measure that.

Related questions

It fails in tests and works in the application.
Then the test database is the one missing the table, and the usual reason is that the suite is not running migrations at all - RefreshDatabase has not been applied, or the test connection points somewhere other than you think. Neither is a problem with the migration itself.
The migration is there and marked as run.
Then it ran against a different database. Compare the database name in the error with the connection the application is actually using, and remember that a queued job or an artisan command can be using a different environment file from the web process.
Why does the table name have an s on the end I did not expect?
Because Eloquent derives it from the class name when the model does not say otherwise, and its pluralisation is English and occasionally surprising. Read the name in the error literally: it is what the framework asked for, and comparing it to what exists usually ends the search.
Can this be caused by the queue?
Yes, and it is a nasty version. A worker holding the previous release runs against a database that has already been migrated - or the reverse, a migration that drops a table while old workers still query it. Both produce this error in a job and nowhere else.
Call us+1 848 272 7583WhatsApp+90 850 308 5436Emailinfo@codefacture.comContact page