-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
Symptom-driven fix list. Find the symptom; the fix follows. Cross-references take you to the deeper explanation when one exists.
The database server is not reachable at the host / port you configured.
- Check the host / port in your connection array.
-
Check the server is actually running (
systemctl status mysql,pg_isready). -
Check the firewall (especially in containers —
localhostfrom inside a container is notlocalhostfrom the host). -
Check the credentials file isn't being read from the wrong path (
var_dump($creds)right beforeDB::createImmutable($creds)).
Either the username/password is wrong, or the user has no GRANTs on the database.
-- MySQL — confirm the user exists and has the right grants
SHOW GRANTS FOR 'X'@'host';- The parent directory does not exist.
- The PHP process has no write permission on the file or directory.
- A
:memory:database was opened twice but you expected schema from one to be visible in the other — it isn't; each PDO handle has its own:memory:.
The PDO driver extension is not loaded. Confirm with:
php -m | grep pdoInstall with your package manager:
# Debian / Ubuntu
apt install php-mysql php-pgsql php-sqlite3
# RHEL / Fedora
dnf install php-mysqlnd php-pgsql php-pdoYou passed only driver, host, port without database. Either set database to the name, or pass a full dsn.
The table does not exist in the connected database. Three common causes:
-
You're on the wrong database —
var_dump(DB::getDatabase()->getConnection()->getDriver())and check the DSN. -
The table has been migrated to a different name — search the schema with
SHOW TABLES. -
A model's
$schemawas auto-derived to something you didn't expect —(new MyModel())->getSchema()shows the value.
Turn on debug = true and re-run — the exception message will include the compiled SQL and bound args. Most often:
- A column name collides with a reserved word inside a
DB::raw(...)fragment (raw fragments are emitted verbatim — see Reserved Keywords). - A raw SQL string passed to
DB::query()has a syntax error.
The number of placeholders in the SQL doesn't match the number of bound parameters. Usually one of:
- You bound a parameter that the SQL doesn't reference.
- You forgot to bind a parameter the SQL does reference.
- You used the same
:nametwice — PDO allows it but the parameter must be bound only once.
enableQueryLog() shows both the SQL and the args side by side.
Type coercion mismatch. 'id' = '13' (string) vs id = 13 (int) usually works through PDO's emulated prepares, but on real prepares against PostgreSQL the column type matters. Cast at the boundary:
DB::where('id', (int) $id);PDOStatement::rowCount() is unreliable for SELECT on SQLite and unbuffered MySQL. Use:
$rows = $res->asAssoc()->rows();
$count = count($rows);For INSERT / UPDATE / DELETE on common drivers, numRows() / affectedRows() is reliable.
Known limitation in initorm/query-builder 2.x — parameters bound inside group() callbacks don't propagate to the outer builder. Workaround:
DB::where(DB::raw('(title LIKE :q OR content LIKE :q)'))
->setParameter(':q', '%' . $needle . '%');See Query Builder and Recipe — Search & Filters.
You called DB::select(...) (or any method) before DB::createImmutable([...]). Bootstrap the facade once during application startup.
You called DB::createImmutable([...]) a second time. In 5.0 this throws. Use DB::replaceImmutable($connection) for explicit swapping. See Migration Guide.
The facade is process-global. Reset it in setUp / tearDown:
protected function setUp(): void
{
DB::replaceImmutable(null);
DB::createImmutable($testCredentials);
}The package's own tests/DBTest.php follows this pattern.
You turned soft deletes on but forgot to point them at a column:
protected bool $useSoftDeletes = true;
protected ?string $deletedField = 'deleted_at'; // requiredThat is the entire point of soft deletes. Two escape hatches:
-
$model->onlyDeleted()->read()for the trash view. -
$model->getDatabase()->select(...)to bypass the model layer entirely.
Three common causes:
- The WHERE doesn't match any row (try a
SELECTwith the same WHERE first). - Soft deletes are on and the target row is already soft-deleted (the model adds
WHERE deleted_at IS NULL). - The model's
$updatablegate isfalse.
PDO's FETCH_CLASS bypasses __set. Use a get{Column}Attribute() accessor for read-time transformation. See Entities.
Inside the mutator body, you wrote $this->column = $value instead of $this->setAttribute('column', $value). Fix it — it never worked correctly and PHP is about to make it fatal.
You are on the old 4.x. Upgrade to 5.0 — the helper now computes them separately. See Migration Guide.
You are on 4.x and the DataTables payload arrived with length as a string. Upgrade to 5.0 — the new RequestParser coerces.
Open the browser DevTools network tab and look at the AJAX request. Common causes:
- The endpoint returns HTML (a 500 page) instead of JSON.
-
Content-Typeis notapplication/json. - The endpoint throws but PHP has
display_errors = on, mixing the stack trace into the response.
Wrap the endpoint in a try / catch that always returns JSON and check the response body.
The response is valid JSON but missing a field DataTables expects (draw, recordsTotal, recordsFiltered, data). Make sure you're returning $dt->toArray() (or (string) $dt) — not just $dt->handle().
The helper ignores columns[i].search.value — only the global search.value is applied. See DataTables — Advanced.
If you're using offset-pagination on a non-indexed sort column, both the SELECT and the recordsTotal count get slow:
- Index the sort column — usually fixes it on its own.
- Cursor pagination — see Recipe — Pagination → variations.
- Drop the count — clients that show "Page X of N" can sometimes live with "Page X, more →" instead.
The buffer grows without bound. In long-lived processes, leave logging off and turn it on temporarily around suspect code paths, or disableQueryLog() and drop the buffer periodically.
Move read-only work outside the transaction; keep the transaction body to writes only.
$snapshot = DB::select('*')->from('users')->where('id', $id)->read()->row();
// ...do business logic on $snapshot...
DB::transaction(function ($db) use ($id, $patch) {
$db->where('id', $id)->update('users', $patch);
});- Enable
debug = trueon the connection (development only). - Enable
enableQueryLog()around the suspect code. - Copy the prepared SQL out of the log and run it by hand in a database CLI / GUI.
- If the SQL passes there, the bug is in your application logic; if it fails there too, you have an SQL bug.
- If you're stuck, open an issue with the SQL + args + the expected behaviour. Documentation gaps are bugs and get fixed on the same day.
- Debugging — the full debugging toolkit.
- Query Logging — how the buffer works.
- FAQ — for the "is this normal?" questions.
initphp/database · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Contributing · Security Policy
Getting Started
Core API
ORM
Advanced
DataTables Helper
Recipes
- Index
- — Pagination
- — Search & Filters
- — Upsert / REPLACE INTO
- — Audit Log
- — DataTables Bootstrap
- — Repository Pattern
Reference
Migration & Help