DEV Community

umzzil nng
umzzil nng

Posted on Originally published at oraerror.com

PostgreSQL 22031 Error: Causes and Solutions Complete Guide

PostgreSQL Error 22031: Invalid Argument for SQL JSON Datetime Function

PostgreSQL error code 22031 (invalid argument for sql json datetime function) occurs when the .datetime() method inside a SQL/JSON path expression receives a value it cannot parse as a valid date or timestamp. This typically happens when JSON string values don't conform to ISO 8601 format, or when a provided format template doesn't match the actual data. It's most commonly seen when using jsonb_path_query(), jsonb_path_exists(), or jsonb_path_match() functions.


Top 3 Causes

1. Non-ISO 8601 Date Strings in JSON

PostgreSQL's .datetime() method strictly expects ISO 8601 formatted strings by default. Passing regional or legacy date formats without a format template will trigger this error.

-- This FAILS: non-standard date format
SELECT jsonb_path_query(
    '{"date": "01/15/2024"}',
    '$.date.datetime()'
);
-- ERROR:  22031: invalid argument for sql json datetime function

-- This WORKS: provide a matching format template
SELECT jsonb_path_query(
    '{"date": "01/15/2024"}',
    '$.date.datetime("MM/DD/YYYY")'
);

-- This WORKS: ISO 8601 format needs no template
SELECT jsonb_path_query(
    '{"date": "2024-01-15"}',
    '$.date.datetime()'
);
Enter fullscreen mode Exit fullscreen mode

2. Mismatched Format Template

When supplying a custom format pattern to .datetime(), the pattern must match the actual string exactly — including separators and field order.

-- This FAILS: format template doesn't match the actual string
SELECT jsonb_path_query(
    '{"ts": "2024-01-15 10:30:00"}',
    '$.ts.datetime("YYYY/MM/DD")'
);
-- ERROR: 22031: invalid argument for sql json datetime function

-- This WORKS: template matches the data exactly
SELECT jsonb_path_query(
    '{"ts": "2024-01-15 10:30:00"}',
    '$.ts.datetime("YYYY-MM-DD HH24:MI:SS")'
);

-- Practical range filter example
SELECT id, payload
FROM orders
WHERE jsonb_path_exists(
    payload,
    '$.created_at.datetime("YYYY-MM-DD HH24:MI:SS") > $start',
    '{"start": "2024-01-01T00:00:00"}'::jsonb
);
Enter fullscreen mode Exit fullscreen mode

3. Invalid or Missing Timezone Offset

When working with timezone-aware timestamps, an invalid timezone offset (e.g., +99:00) or a malformed timezone suffix in the JSON value causes error 22031. PostgreSQL applies strict validation on timezone offsets within SQL/JSON path processing.

-- This FAILS: invalid timezone offset
SELECT jsonb_path_query(
    '{"ts": "2024-01-15T10:30:00+99:00"}',
    '$.ts.datetime()'
);
-- ERROR: 22031: invalid argument for sql json datetime function

-- This WORKS: valid timezone offset
SELECT jsonb_path_query(
    '{"ts": "2024-01-15T10:30:00+09:00"}',
    '$.ts.datetime()'
);

-- Detect invalid timezone values before processing
SELECT id, payload->>'ts' AS bad_ts
FROM logs
WHERE NOT (
    payload->>'ts' ~ 
    '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}([+-](0[0-9]|1[0-4]):[0-5]\d|Z)?$'
);
Enter fullscreen mode Exit fullscreen mode

Quick Fix Solutions

-- Option 1: Cast at SQL level instead of using .datetime()
SELECT *
FROM events
WHERE (payload->>'event_date')::date > '2024-01-01';

-- Option 2: Normalize stored JSON dates with an UPDATE
UPDATE events
SET payload = jsonb_set(
    payload,
    '{event_date}',
    to_jsonb(to_date(payload->>'event_date', 'MM/DD/YYYY')::text)
)
WHERE payload->>'event_date' ~ '^\d{2}/\d{2}/\d{4}$';

-- Option 3: Safe wrapper function to avoid runtime errors
CREATE OR REPLACE FUNCTION safe_json_to_timestamp(val text)
RETURNS timestamptz AS $$
BEGIN
    RETURN val::timestamptz;
EXCEPTION WHEN OTHERS THEN
    RETURN NULL;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

SELECT safe_json_to_timestamp(payload->>'ts')
FROM logs;
Enter fullscreen mode Exit fullscreen mode

Prevention Tips

Enforce ISO 8601 on insert. Use a trigger or application-level validation to ensure all date/time values stored in JSONB columns follow ISO 8601 format (YYYY-MM-DD or YYYY-MM-DDTHH:MI:SS±HH:MM). This eliminates the need for format templates and makes .datetime() calls reliable.

Validate before querying. When consuming data from external systems or legacy pipelines, run a pre-check query using regex to identify non-conforming records before running JSON path queries with .datetime(). This is especially important in ETL jobs and batch processing workflows where bad records can silently break downstream logic.


Related Errors

Code Name Notes
22007 invalid_datetime_format Similar but occurs in standard SQL casting
22P02 invalid_text_representation Text-to-type conversion failure
42883 undefined_function Occurs on PostgreSQL < 12 where .datetime() isn't supported

📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.

Top comments (0)