How to Fix PostgreSQL 'generate_series does not exist' Without Integer Arguments
Threat/Impact Level: MEDIUM | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins
TL;DR
- What broke: PostgreSQL cannot resolve the
generate_series()function overload because the arguments are not typed as integers — likely passed astext,numeric, or untyped literals, causing function resolution to fail at parse time. - How to fix it: Explicitly cast arguments to
integer(orbigint/timestamptzdepending on the overload you need):generate_series(1::integer, 100::integer). - Fast path: Use our Client-Side Sandbox above to paste your failing query and auto-refactor the cast signatures without sending your schema to a third-party server.
The Incident (What Does the Error Mean?)
Raw error from PostgreSQL logs:
ERROR: function generate_series(unknown, unknown) does not exist
LINE 1: SELECT * FROM generate_series(1, 100);
HINT: No function matches the given name and argument types.
You might need to add explicit type casts.
PostgreSQL's function overload resolution is strict. generate_series has multiple registered signatures:
| Signature | Return Type |
|---|---|
generate_series(integer, integer) |
integer |
generate_series(integer, integer, integer) |
integer |
generate_series(bigint, bigint) |
bigint |
generate_series(timestamp, timestamp, interval) |
timestamp |
When you pass untyped literals or values sourced from a text column, a subquery returning numeric, or a parameterized query with unbound types, the parser cannot match any signature and aborts. This kills the entire query — no partial execution, no fallback.
The Attack Vector / Blast Radius
This isn't a security exploit, but the blast radius in production is significant:
- Scheduled jobs fail silently if the error is swallowed by a PL/pgSQL
EXCEPTIONblock — you lose your entire time-series row generation for that batch window. - ORM-generated queries (SQLAlchemy, ActiveRecord) that dynamically bind parameters as
textornumericwill fail on every call, not just edge cases. This means a deploy that worked in dev (where Postgres inferred types more loosely) will hard-fail in prod against a stricter server version or extension config. - Cascading failures in data pipelines: If
generate_seriesis used to scaffold a date spine for a dbt model or Airflow task, the downstreamLEFT JOINagainst fact tables produces zero rows — silently corrupting aggregated metrics until someone notices the dashboard flatlined. - Version mismatch risk: PostgreSQL 14+ tightened implicit cast rules. A query working on PG 11 may throw this error after a cluster upgrade with zero code changes.
How to Fix It (The Solution)
Basic Fix — Explicit Integer Cast
- SELECT * FROM generate_series(1, 100);
+ SELECT * FROM generate_series(1::integer, 100::integer);
When Arguments Come From a Column or Subquery
- SELECT generate_series(start_val, end_val) FROM my_ranges;
+ SELECT generate_series(start_val::integer, end_val::integer) FROM my_ranges;
Timestamp Overload (Common Confusion)
- SELECT generate_series('2024-01-01', '2024-12-31', '1 day');
+ SELECT generate_series(
+ '2024-01-01'::timestamptz,
+ '2024-12-31'::timestamptz,
+ '1 day'::interval
+ );
Enterprise Best Practice — Parameterized Queries with Typed Binds
If you are calling this from application code (Go, Python, Java), never rely on the driver to infer types for set-returning functions. Bind parameters with explicit OID types:
# Python psycopg2 example
- cur.execute("SELECT generate_series(%s, %s)", (start, end))
+ from psycopg2.extensions import AsIs
+ cur.execute(
+ "SELECT generate_series(%s::integer, %s::integer)",
+ (int(start), int(end))
+ )
For PL/pgSQL functions, declare intermediate variables with explicit types:
CREATE OR REPLACE FUNCTION build_series(p_start text, p_end text)
RETURNS SETOF integer AS $$
DECLARE
+ v_start integer := p_start::integer;
+ v_end integer := p_end::integer;
BEGIN
- RETURN QUERY SELECT generate_series(p_start, p_end);
+ RETURN QUERY SELECT generate_series(v_start, v_end);
END;
$$ LANGUAGE plpgsql;
💡 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. pgTAP unit tests for all SRF (Set-Returning Function) calls:
-- In your pgTAP test suite
SELECT throws_ok(
$$ SELECT generate_series(NULL, 10) $$,
'Test that untyped NULL is caught'
);
2. plpgsql_check extension in your pipeline:
# Add to your CI Dockerfile or migration runner
psql -c "CREATE EXTENSION plpgsql_check;"
psql -c "SELECT plpgsql_check_function('build_series(text,text)');"
This statically analyzes PL/pgSQL bodies and catches type resolution failures before they hit production.
3. SQLFluff linting rule in pre-commit:
# .sqlfluff
[sqlfluff]
dialect = postgres
[sqlfluff:rules:L063]
# Enforce explicit casting on function arguments
force_enable = true
4. Liquibase/Flyway migration validation: Run EXPLAIN (not EXECUTE) against all new migrations in a CI Postgres container. A type-resolution error surfaces at plan time:
psql -c "EXPLAIN SELECT generate_series(start_col, end_col) FROM staging.ranges;"
# Will throw ERROR before any rows are touched
5. Lock your Postgres minor version in docker-compose and Terraform RDS configs to prevent silent behavior changes from implicit cast rule updates between patch versions:
- engine_version = "14"
+ engine_version = "14.11"