PostgreSQL Error 2201G: Invalid Argument for Width Bucket Function
PostgreSQL error code 2201G is thrown when the width_bucket() function receives an invalid argument that prevents it from computing a valid bucket assignment. This typically happens when the bucket count is zero or negative, the lower and upper bounds are identical, or the operand value is NaN. Understanding this error is essential for anyone working with data distribution analysis, histograms, or statistical bucketing in PostgreSQL.
Top 3 Causes
1. Bucket Count is Zero or Negative
The count parameter in width_bucket(operand, low, high, count) must be a positive integer (≥ 1). Passing zero or a negative value immediately triggers error 2201G.
-- ❌ This will fail: count = 0
SELECT width_bucket(75.5, 0, 100, 0);
-- ERROR: invalid argument for width_bucket
-- ❌ Dynamic count that resolves to 0
SELECT width_bucket(score, 0, 100, COUNT(DISTINCT category) - 5)
FROM scores
HAVING COUNT(DISTINCT category) < 5;
-- ✅ Fix: Use GREATEST to enforce a minimum of 1
SELECT width_bucket(score, 0, 100, GREATEST(1, bucket_count))
FROM scores
CROSS JOIN (SELECT COUNT(DISTINCT category) AS bucket_count FROM scores) sub;
2. Lower Bound Equals Upper Bound
When low = high, the bucket width becomes zero, making it mathematically impossible to assign values to buckets. This is a common issue when automatically deriving bounds from data that has no variation.
-- ❌ This will fail when all prices are identical
SELECT width_bucket(price, MIN(price), MAX(price), 10)
FROM products
WHERE category = 'promo';
-- ERROR: invalid argument for width_bucket (when min = max)
-- ✅ Fix: Handle equal bounds with a CASE expression
SELECT
product_id,
CASE
WHEN min_p = max_p THEN 1
ELSE width_bucket(price, min_p, max_p, 10)
END AS price_bucket
FROM products
CROSS JOIN (
SELECT MIN(price) AS min_p, MAX(price) AS max_p
FROM products WHERE category = 'promo'
) bounds;
3. Operand is NaN
The NUMERIC type in PostgreSQL supports special values like NaN (Not a Number). If the operand passed to width_bucket() is NaN, the function raises error 2201G because NaN cannot be placed in any finite interval.
-- ❌ NaN in data triggers the error
SELECT width_bucket('NaN'::numeric, 0, 100, 10);
-- ERROR: invalid argument for width_bucket
-- ✅ Fix: Filter out NaN values before bucketing
SELECT
sensor_id,
width_bucket(value, 0.0, 500.0, 10) AS bucket
FROM sensor_readings
WHERE value IS NOT NULL
AND value::text != 'NaN';
-- ✅ Fix: Replace NaN with NULL using NULLIF pattern
SELECT
sensor_id,
width_bucket(
CASE WHEN value::text = 'NaN' THEN NULL ELSE value END,
0.0, 500.0, 10
) AS bucket
FROM sensor_readings;
Quick Fix: Defensive Wrapper Function
The most robust solution is to create a reusable safe wrapper that handles all edge cases:
CREATE OR REPLACE FUNCTION safe_width_bucket(
operand NUMERIC,
low NUMERIC,
high NUMERIC,
count INTEGER
) RETURNS INTEGER AS $$
BEGIN
IF operand IS NULL OR operand::text = 'NaN' THEN RETURN NULL; END IF;
IF low = high OR count <= 0 THEN RETURN NULL; END IF;
RETURN width_bucket(operand, low, high, count);
EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
-- Usage
SELECT product_id, safe_width_bucket(price, 0, 5000, 20) AS bucket
FROM products;
Prevention Tips
Validate inputs before calling
width_bucket(): Always useGREATEST(1, count)for the bucket count and wrap your bounds logic with aCASEstatement to handle thelow = highscenario. AddCHECKconstraints to tables storing numeric data to preventNaNfrom being stored in the first place.Standardize with a team-wide wrapper function: Adopt
safe_width_bucket()(or equivalent) as your organization's standard and enforce its use through code reviews. Add linting rules or CI checks that flag direct calls towidth_bucket()without prior input validation, reducing the chance of this error reaching production.
Related Errors
-
2201F– Invalid argument for logarithm (same error class, math function family) -
2201E– Invalid argument for power function -
22012– Division by zero (related when bucket width resolves to zero internally) -
22003– Numeric value out of range
📖 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)