How to Fix PostgreSQL 'numeric field overflow' Error: NUMERIC Precision and Scale Mismatch Explained
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins
TL;DR
- What broke: PostgreSQL rejected a write because the value being stored has more integer digits than
NUMERIC(precision, scale)allows — specifically, the integer part exceedsprecision - scaledigits. - How to fix it: Widen the column definition with
ALTER TABLE … ALTER COLUMN … TYPE NUMERIC(new_p, new_s)or sanitize the upstream value before it hits the DB. - Use our Client-Side Sandbox below to paste your DDL/DML and auto-refactor the correct
NUMERICbounds without leaking your schema to a third-party AI.
The Incident (What does the error mean?)
Raw error from PostgreSQL:
ERROR: numeric field overflow
DETAIL: A field with precision 8, scale 2 must round to an absolute value less than 10^6.
PostgreSQL NUMERIC(precision, scale) enforces:
precision= total significant digits (both sides of the decimal)scale= digits to the right of the decimal- Max integer digits =
precision - scale
For NUMERIC(8, 2): max integer digits = 6, so the ceiling is 999999.99. Any value ≥ 1000000.00 detonates this error. The transaction is rolled back entirely — no partial writes.
The Attack Vector / Blast Radius
This is not a soft warning. PostgreSQL raises an exception, which means:
- The entire transaction aborts. If this INSERT/UPDATE is inside a larger transaction block (e.g., a financial batch job), every preceding statement in that transaction is also rolled back.
- Connection pool saturation. In high-throughput services (PgBouncer, pgpool), repeated transaction failures from an ORM retry loop will exhaust pool slots within seconds.
- Silent data loss in async pipelines. Kafka consumers or Celery workers that lack dead-letter-queue handling will silently drop the record after max retries, causing undetected financial or audit data loss.
- ORM blind spots. ORMs like SQLAlchemy or ActiveRecord often define
NUMERICcolumns with defaults (precision=10, scale=2) that look safe in dev but fail against real production magnitudes (e.g., a currency column suddenly handling billion-dollar transactions).
The blast radius scales directly with how central this column is. A total_amount column on an orders table going down is a P0 outage.
How to Fix It (The Solution)
Basic Fix — Widen the Column
Calculate the required precision: new_precision = desired_integer_digits + scale.
For a column that needs to store up to 99,999,999.99, you need NUMERIC(10, 2).
-- Schema change
ALTER TABLE orders
- ALTER COLUMN total_amount TYPE NUMERIC(8, 2);
+ ALTER COLUMN total_amount TYPE NUMERIC(14, 2);
-- If you cannot change the schema immediately, sanitize at insert time (stopgap only)
INSERT INTO orders (order_id, total_amount)
- VALUES (1042, 1500000.75);
+ VALUES (1042, LEAST(1500000.75, 999999.99)); -- ⚠️ stopgap only, data is truncated
⚠️ The
LEAST()stopgap destroys data accuracy. Use it only to unblock a pipeline while the schema migration is prepared.
Enterprise Best Practice — Schema Migration with Zero Downtime
For production tables with millions of rows, ALTER COLUMN TYPE acquires an ACCESS EXCLUSIVE lock. Use a shadow-column migration pattern:
-- Step 1: Add new column with correct precision (non-blocking)
ALTER TABLE orders
+ ADD COLUMN total_amount_v2 NUMERIC(14, 2);
-- Step 2: Backfill (run in batches to avoid lock escalation)
+ UPDATE orders
+ SET total_amount_v2 = total_amount
+ WHERE total_amount_v2 IS NULL;
-- Step 3: Swap application writes to new column, then rename
+ ALTER TABLE orders RENAME COLUMN total_amount TO total_amount_deprecated;
+ ALTER TABLE orders RENAME COLUMN total_amount_v2 TO total_amount;
-- Step 4: Drop old column after validation window
+ ALTER TABLE orders DROP COLUMN total_amount_deprecated;
Enforce precision at the ORM layer too:
# SQLAlchemy example
- total_amount = Column(Numeric(precision=8, scale=2))
+ total_amount = Column(Numeric(precision=14, scale=2))
# ActiveRecord migration
- t.decimal :total_amount, precision: 8, scale: 2
+ t.decimal :total_amount, precision: 14, scale: 2
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Schema Linting with squawk
squawk is a PostgreSQL migration linter. Add it to your CI pipeline:
# .github/workflows/db-lint.yml
- name: Lint SQL migrations
run: |
squawk migrations/*.sql --exclude prefer-robust-stmts
Write a custom squawk rule or grep check to flag NUMERIC columns with precision - scale < 10 on financial tables.
2. Integration Test with Boundary Values
Your test suite must include boundary value assertions:
# pytest example
def test_order_total_boundary():
"""Ensure max expected order value fits the column."""
max_expected = Decimal("99999999.99") # business requirement
# This will raise if column is too narrow
db.execute("INSERT INTO orders(total_amount) VALUES (%s)", [max_expected])
3. Terraform / Flyway Schema Governance
If you manage schema via Flyway or Liquibase, add a pre-migration validation script that queries information_schema.columns and asserts minimum precision for sensitive columns:
-- Assert financial columns meet precision floor
SELECT column_name, numeric_precision, numeric_scale
FROM information_schema.columns
WHERE table_name = 'orders'
AND column_name IN ('total_amount', 'tax_amount', 'discount_amount')
AND (numeric_precision - numeric_scale) < 10; -- Fail if this returns rows
Pipe this into your CI gate: non-zero row count = pipeline failure.
4. OPA / Conftest Policy for IaC-Managed Schemas
If your DB schema is managed via Terraform (postgresql provider):
# policy/numeric_precision.rego
package terraform.postgres
deny[msg] {
col := input.resource_changes[_].change.after.column[_]
col.type == "numeric"
(col.precision - col.scale) < 10
msg := sprintf("Column '%v' has insufficient integer digit capacity. Increase precision.", [col.name])
}