Error classification

LocalData MCP classifies every database error into a structured response that tells LLM agents what went wrong, whether it makes sense to retry, and what to do next. Instead of parsing raw driver messages, agents receive a uniform JSON object they can act on programmatically.

Error categories

Every classified error carries one of these categories:

Category

Value

Description

Example

AUTH_ERROR

auth_error

Authentication or authorization failure

Wrong password, missing privileges

SCHEMA_ERROR

schema_error

Missing or invalid database objects

Non-existent table or column

SYNTAX_ERROR

syntax_error

Malformed SQL

Typo in a keyword, unbalanced parentheses

TRANSIENT_ERROR

transient_error

Temporary failure that may resolve itself

Lock timeout, deadlock, busy database

RESOURCE_ERROR

resource_error

System resource exhaustion

Out of memory, disk full

CONSTRAINT_ERROR

constraint_error

Data integrity violation

Duplicate key, foreign key mismatch

CONNECTION_ERROR

connection_error

Unable to reach the database

Network unreachable, server down

QUERY_EXECUTION

query_execution

General execution failure (fallback)

Division by zero, type mismatch

Retryability

The is_retryable flag signals whether repeating the same operation is likely to succeed:

Retryable

Categories

Recommended action

Yes

TRANSIENT_ERROR, CONNECTION_ERROR

Wait briefly and retry (with backoff)

No

All others

Fix the query, schema, credentials, or data before retrying

Agents should treat is_retryable: false as a hard stop for the current request. Retrying a syntax error or constraint violation will always produce the same failure.

Structured error response format

{
  "error": true,
  "error_type": "schema_error",
  "is_retryable": false,
  "message": "no such table: orders",
  "suggestion": "Verify table/column names in the SQLite database.",
  "database_error_code": null,
  "database": "sales.db"
}

Field

Type

Description

error

bool

Always true for error responses

error_type

string

One of the category values listed above

is_retryable

bool

Whether the agent should retry

message

string

Human-readable description of the error

suggestion

string

Actionable next step

database_error_code

string | null

Backend-specific code (SQLSTATE, errno) when available

database

string | null

Name of the database where the error occurred

Database-specific mappings

Each supported backend has a dedicated mapper that uses backend-native signals (message patterns, SQLSTATE codes, error numbers, or exception class names) to classify errors. When no backend-specific mapper matches, a generic keyword-based fallback is used.

SQLite

SQLite errors are classified by scanning the exception message for known substrings:

Pattern

Category

authorization, readonly

AUTH_ERROR

locked, busy

TRANSIENT_ERROR

disk, full, no space

RESOURCE_ERROR

constraint, unique, foreign key, not null

CONSTRAINT_ERROR

no such table, no such column

SCHEMA_ERROR

syntax, near

SYNTAX_ERROR

PostgreSQL

PostgreSQL errors are classified by their SQLSTATE code (pgcode):

SQLSTATE prefix

Category

Notes

42601

SYNTAX_ERROR

Exact match for syntax errors

42xxx (other)

SCHEMA_ERROR

Undefined table, column, etc.

28xxx

AUTH_ERROR

Invalid authorization

08xxx

CONNECTION_ERROR

Connection exception (retryable)

23xxx

CONSTRAINT_ERROR

Integrity constraint violation

40xxx

TRANSIENT_ERROR

Transaction rollback (retryable)

53xxx

RESOURCE_ERROR

Insufficient resources

MySQL / MariaDB

MySQL errors are classified by their numeric error code (errno):

Error code

Category

Notes

1044, 1045

AUTH_ERROR

Access denied

1146, 1054

SCHEMA_ERROR

Unknown table or column

1064

SYNTAX_ERROR

SQL syntax error

1205

TRANSIENT_ERROR

Lock wait timeout (retryable)

1114

RESOURCE_ERROR

Table is full

1062, 1452

CONSTRAINT_ERROR

Duplicate entry, FK violation

2002, 2003, 2006

CONNECTION_ERROR

Connection failures (retryable)

DuckDB

DuckDB errors are classified by the Python exception class name:

Exception class

Category

ParserException

SYNTAX_ERROR

CatalogException

SCHEMA_ERROR

BinderException

SCHEMA_ERROR

ConstraintException

CONSTRAINT_ERROR

OutOfMemoryException

RESOURCE_ERROR

IOException (memory/space)

RESOURCE_ERROR

IOException (other)

CONNECTION_ERROR

Oracle

Oracle errors are classified by their ORA-XXXXX error codes:

Error code

Category

Notes

ORA-01017

AUTH_ERROR

Invalid username/password

ORA-01031

AUTH_ERROR

Insufficient privileges

ORA-01045

AUTH_ERROR

User lacks CREATE SESSION privilege

ORA-00942

SCHEMA_ERROR

Table or view does not exist

ORA-00904

SCHEMA_ERROR

Invalid identifier

ORA-00900

SYNTAX_ERROR

Invalid SQL statement

ORA-00933

SYNTAX_ERROR

SQL command not properly ended

ORA-01653

RESOURCE_ERROR

Unable to extend table

ORA-04031

RESOURCE_ERROR

Unable to allocate shared memory

ORA-00060

TRANSIENT_ERROR

Deadlock detected (retryable)

ORA-08177

TRANSIENT_ERROR

Serialization failure (retryable)

ORA-03113

CONNECTION_ERROR

End-of-file on communication channel (retryable)

ORA-03114

CONNECTION_ERROR

Not connected to Oracle (retryable)

ORA-12541

CONNECTION_ERROR

No listener (retryable)

ORA-12154

CONNECTION_ERROR

TNS could not resolve connect identifier (retryable)

MS SQL Server

MSSQL errors are classified by their numeric error code (Msg) and severity level:

Error code

Category

Notes

18456

AUTH_ERROR

Login failed

18452

AUTH_ERROR

Login from untrusted domain

208

SCHEMA_ERROR

Invalid object name

207

SCHEMA_ERROR

Invalid column name

102, 156

SYNTAX_ERROR

Incorrect syntax

1205

TRANSIENT_ERROR

Transaction deadlocked (retryable)

1222

TRANSIENT_ERROR

Lock request timeout (retryable)

547, 2627, 2601

CONSTRAINT_ERROR

Constraint / unique key violation

1105

RESOURCE_ERROR

Could not allocate space

9002

RESOURCE_ERROR

Transaction log full

When the error code is not in the table, severity is used as a fallback:

  • Severity >= 20: CONNECTION_ERROR (fatal error)

  • Severity >= 17: RESOURCE_ERROR

Integration guide

To add a mapper for a new database backend:

  1. Create a class that inherits from DatabaseErrorMapper and implements map_error(self, exception) -> StructuredErrorResponse.

  2. Register the mapper with ErrorMapperRegistry:

from localdata_mcp.error_classification import (
    DatabaseErrorMapper,
    ErrorMapperRegistry,
    StructuredErrorResponse,
)
from localdata_mcp.error_handler import ErrorCategory


class MyDBErrorMapper(DatabaseErrorMapper):
    def map_error(self, exception: Exception) -> StructuredErrorResponse:
        msg = str(exception).lower()
        if "duplicate" in msg:
            return StructuredErrorResponse(
                error_type=ErrorCategory.CONSTRAINT_ERROR,
                message=str(exception),
                suggestion="Check for duplicate values.",
            )
        # Fall back to the generic mapper for unrecognised errors.
        from localdata_mcp.error_classification import GenericDatabaseErrorMapper
        return GenericDatabaseErrorMapper().map_error(exception)


ErrorMapperRegistry.register("mydb", MyDBErrorMapper())
  1. All calls to classify_error(exc, db_type="mydb") will now use the new mapper automatically.

Helper functions

Three convenience functions in localdata_mcp.error_classification cover the most common agent needs:

classify_error(exception, db_type="generic")

Returns the full StructuredErrorResponse for an exception. Use this when the agent needs all fields (category, retryability, suggestion, error code).

from localdata_mcp.error_classification import classify_error

resp = classify_error(exc, db_type="sqlite")
print(resp.error_type)   # ErrorCategory.SYNTAX_ERROR
print(resp.suggestion)   # "Review the SQL statement for syntax errors."

is_error_retryable(exception, db_type="generic")

Returns True if the error is transient and worth retrying. A shorthand for classify_error(...).is_retryable.

from localdata_mcp.error_classification import is_error_retryable

if is_error_retryable(exc, db_type="postgresql"):
    # back off and retry
    ...

get_error_suggestion(exception, db_type="generic")

Returns the actionable suggestion string. A shorthand for classify_error(...).suggestion.

from localdata_mcp.error_classification import get_error_suggestion

hint = get_error_suggestion(exc, db_type="mysql")

All three functions use the same classification pipeline internally, so their results are always consistent for the same exception and database type.