For modern PDO applications, use PDO::ERRMODE_EXCEPTION, catch database exceptions, log the detailed error on the server and show visitors a short message.
<?php
require 'config.php';
$id=2;
try{
$stmt=$dbo->prepare(
"SELECT id,name,class,mark
FROM student
WHERE id=:id"
);
$stmt->bindValue(
':id',
$id,
PDO::PARAM_INT
);
$stmt->execute();
$row=$stmt->fetch(PDO::FETCH_ASSOC);
}catch(PDOException $e){
error_log($e->getMessage());
echo 'Unable to load the record.';
}
The SELECT query remains separate from the error-handling logic. PDO reports the database failure; the application decides what should be logged and what the visitor should see.
Show Table of ContentsPDO supports three error modes. For PHP 8.x applications, exception mode is normally the clearest approach because database failures can be handled with try and catch.
| PDO mode | Behavior | Typical use |
|---|---|---|
PDO::ERRMODE_EXCEPTION | Throws a PDOException | Recommended for normal application code |
PDO::ERRMODE_WARNING | Raises a PHP warning and the failed operation can return false | Occasional debugging or legacy code |
PDO::ERRMODE_SILENT | No warning or exception; code must check failures manually | Legacy/manual error-handling patterns |
Set the mode on the PDO connection:
$dbo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
With exception mode, a failed database operation throws a PDOException. The detailed message can be logged while the visitor receives a generic response.
<?php
require 'config.php';
try{
// Deliberate SQL spelling error for this example
$sql="SELCT id,name FROM student";
$stmt=$dbo->query($sql);
}catch(PDOException $e){
error_log(
'PDO error: '.$e->getMessage()
);
echo 'Database operation failed.';
}
The exact SQLSTATE and driver message can vary by database and database version, so production code should not depend on the human-readable message text.
During development you may inspect detailed errors locally. On a public site, avoid displaying connection details, table names, SQL fragments or database server messages.
catch(PDOException $e){
error_log($e->getMessage());
echo 'Please try again later.';
}
PHP try-catch handling can also be used for exceptions outside PDO.
ERRMODE_WARNING raises a PHP warning when a database operation fails. Execution can continue after the failed call, so the return value still needs to be checked.
$dbo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_WARNING
);
$result=$dbo->query(
"SELCT id,name FROM student"
);
if($result===false){
echo 'The query failed.';
}
This mode can expose PHP warnings if display_errors is enabled, so it is usually less suitable than exception handling in production applications.
In silent mode, PDO does not raise a warning or exception for ordinary database errors. The application must check the return value and inspect the error manually.
$dbo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_SILENT
);
$result=$dbo->query(
"SELCT id,name FROM student"
);
if($result===false){
$info=$dbo->errorInfo();
error_log(
'SQLSTATE: '.$info[0].
' Database error: '.$info[2]
);
echo 'Database operation failed.';
}
Silent mode is useful for understanding errorCode() and errorInfo(), but exception mode usually produces clearer application code.
errorCode() returns the SQLSTATE code associated with the last operation on that handle. SQLSTATE is a standardized five-character error code.
$code=$dbo->errorCode();
echo $code;
For example, a missing table may produce an SQLSTATE such as:
42S02
The exact value depends on the type of failure.
errorInfo() returns an array containing SQLSTATE plus driver-specific information.
$info=$dbo->errorInfo();
print_r($info);
A MySQL error array can have this structure:
Array
(
[0] => SQLSTATE code
[1] => Driver-specific error code
[2] => Driver-specific error message
)
[2] is useful for logs and debugging, but should not normally be printed to visitors.PDO provides error methods on both the database connection and the statement object. Check the handle on which the failed operation occurred.
$result=$dbo->query($sql);
if($result===false){
$code=$dbo->errorCode();
$info=$dbo->errorInfo();
}
When a prepared statement fails during execution in silent mode, inspect the statement:
$stmt=$dbo->prepare($sql);
$ok=$stmt->execute($params);
if($ok===false){
$code=$stmt->errorCode();
$info=$stmt->errorInfo();
}
With ERRMODE_EXCEPTION, the same failures normally become exceptions and are handled in the catch block instead.
A connection failure can occur while the PDO object is being created, so wrap the constructor itself in the try block.
<?php
$host='localhost';
$dbname='testdb';
$username='username';
$password='password';
$dsn="mysql:host=$host;dbname=$dbname;charset=utf8mb4";
try{
$dbo=new PDO(
$dsn,
$username,
$password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
}catch(PDOException $e){
error_log(
'PDO connection error: '.$e->getMessage()
);
exit('Database connection is temporarily unavailable.');
}
See the PDO MySQL connection tutorial for the reusable config.php setup used throughout this section.
The simplest approach is to send PDO errors to PHP's configured server error log.
catch(PDOException $e){
error_log(
'PDO error: '.$e->getMessage()
);
echo 'Unable to process the request.';
}
This avoids exposing database details in the page and lets the server's logging configuration control where messages are stored.
If an application deliberately uses its own log file, use a controlled path and ensure the web server can write to it.
$log_file=__DIR__.'/logs/pdo-errors.log';
error_log(
date('Y-m-d H:i:s').
' PDO error: '.
$e->getMessage().
PHP_EOL,
3,
$log_file
);
When several dependent database operations run inside a transaction, catch failures and roll back only if a transaction is still active.
try{
$dbo->beginTransaction();
// First database operation
// Second dependent database operation
$dbo->commit();
}catch(Throwable $e){
if($dbo->inTransaction()){
$dbo->rollBack();
}
error_log($e->getMessage());
echo 'Database transaction failed.';
}
PDO Transactions and Rollback
A stored procedure called through PDO can also raise a database exception.
try{
$stmt=$dbo->prepare(
"CALL sp_get_users()"
);
$stmt->execute();
$rows=$stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
}catch(PDOException $e){
error_log(
'Stored procedure error: '.
$e->getMessage()
);
echo 'Unable to run the stored procedure.';
}
See the PDO stored procedure tutorial for procedure-specific examples.
An exception can contain SQLSTATE codes, table names, SQL fragments and database-specific details. Log the message instead of exposing it publicly.
If execute() fails on a prepared statement in silent mode, inspect the PDOStatement. If a direct operation on the PDO connection fails, inspect the connection.
Configure PDO error handling when the connection is created. Do not wait until after a failed operation to switch error modes.
Prepared statements help separate supplied values from SQL structure, but queries can still fail because of invalid SQL, missing tables, database constraints, connection problems or other database conditions.
When using ERRMODE_SILENT, explicitly check return values. Otherwise a failure can pass unnoticed and later code may operate on false.
A catch block may run before a transaction starts or after it has already ended. Check inTransaction() before calling rollBack().
The sample database used by several PDO tutorials is available from the SQL section.
Sample Student Table SQL Dump
ERRMODE_EXCEPTION is usually the clearest choice for modern PHP applications because database failures are thrown as exceptions that can be handled with try-catch.
No. Log detailed exception messages on the server and show visitors a short user-friendly message instead.
errorCode() returns the SQLSTATE code associated with the last operation on the PDO or PDOStatement handle.
errorInfo() returns an array containing the SQLSTATE code, a driver-specific error code and a driver-specific error message.
PDO::errorInfo() describes the last operation on the database connection, while PDOStatement::errorInfo() describes the last operation performed by that prepared statement.
Catch the failure, check whether the connection still has an active transaction, roll it back when necessary, log the detailed error and return a user-friendly message.
No. Prepared statements separate supplied values from SQL structure, but database operations can still fail because of invalid SQL, constraints, missing objects, connection failures and other database conditions.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.