PostgreSQL Error 42P18: indeterminate datatype
PostgreSQL error 42P18 indeterminate datatype occurs when the query parser or planner cannot determine the data type of an expression, parameter, or literal value at planning time. This typically happens when using untyped NULL values, ambiguous parameters in prepared statements, or calling overloaded functions without sufficient type context. Adding an explicit type cast is almost always the fix.
Top 3 Causes
1. Untyped NULL or Literal in COALESCE / UNION / CASE
When you use raw NULL without a type cast in expressions where PostgreSQL cannot infer the type from context, the planner gives up.
-- Triggers 42P18
SELECT COALESCE(NULL, NULL);
-- Fix: cast explicitly
SELECT COALESCE(NULL::TEXT, NULL::TEXT);
-- Triggers 42P18 in UNION
SELECT NULL
UNION ALL
SELECT NULL;
-- Fix
SELECT NULL::INTEGER
UNION ALL
SELECT NULL::INTEGER;
-- CASE expression fix
SELECT
CASE
WHEN score > 90 THEN 'A'::TEXT
ELSE NULL::TEXT
END AS grade
FROM exam_results;
2. Ambiguous Parameters in PREPARE Statements
When a parameter like $1 appears in a position where PostgreSQL cannot infer its type from the surrounding query, the error fires.
-- Triggers 42P18: $1 type is unknown
PREPARE bad_plan AS SELECT $1;
-- Fix: declare parameter type explicitly
PREPARE good_plan (TEXT) AS SELECT $1;
EXECUTE good_plan('hello');
-- Fix: embed in a typed context
PREPARE typed_plan AS
SELECT * FROM users WHERE username = $1;
-- PostgreSQL infers $1 is TEXT from the column type
EXECUTE typed_plan('alice');
3. Overloaded Functions with Ambiguous Arguments
PostgreSQL supports function overloading. If you call a function with an untyped argument, it may not be able to pick the right signature.
-- Triggers 42P18: which overload to use?
SELECT to_json(NULL);
-- Fix: cast the argument
SELECT to_json(NULL::TEXT);
SELECT to_json(NULL::JSONB);
-- Custom overloaded function scenario
CREATE OR REPLACE FUNCTION describe(val TEXT) RETURNS TEXT
LANGUAGE sql AS $$ SELECT 'text: ' || val; $$;
CREATE OR REPLACE FUNCTION describe(val INTEGER) RETURNS TEXT
LANGUAGE sql AS $$ SELECT 'int: ' || val::TEXT; $$;
-- Ambiguous call (may trigger 42P18)
-- SELECT describe(NULL);
-- Clear, unambiguous calls
SELECT describe(NULL::TEXT);
SELECT describe(NULL::INTEGER);
Quick Fix Solutions
| Situation | Fix |
|---|---|
Raw NULL in expression |
NULL::desired_type |
Untyped PREPARE parameter |
PREPARE plan (TYPE) AS ... |
| Overloaded function call | function(arg::TYPE) |
| Dynamic SQL in PL/pgSQL | Use USING arg::TYPE in EXECUTE
|
-- Dynamic SQL fix in PL/pgSQL
DO $$
DECLARE
result TEXT;
BEGIN
EXECUTE 'SELECT $1::TEXT || $2::TEXT'
INTO result
USING 'Hello'::TEXT, ' World'::TEXT;
RAISE NOTICE '%', result;
END;
$$;
Prevention Tips
1. Always cast NULL and untyped literals. Make it a team rule: every NULL value used in UNION, COALESCE, CASE, or function arguments must carry an explicit ::type cast. Integrate a SQL linter like sqlfluff into your CI pipeline to enforce this automatically.
2. Declare parameter types in PREPARE statements. Never rely solely on type inference for prepared statements. Always use the explicit type list syntax — PREPARE name (type1, type2) AS ... — so PostgreSQL has zero ambiguity at plan time. When using ORMs or drivers (psycopg3, JDBC), use the driver's type annotation API to pass type hints alongside bind parameters.
Related Errors
-
42883undefined_function — function not found due to type mismatch; often appears alongside42P18in overloading scenarios. -
42804datatype_mismatch — type is known but doesn't match what's expected; the resolved counterpart to42P18. -
42P08ambiguous_parameter — closely related; parameter reference is ambiguous rather than typeless.
📖 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)