PostgreSQL

Fix: TypeORM "array is not supported in postgres"

Reproduced the exact failure and the fix on PostgreSQL 16: an array column needs an explicit base type plus array: true, or synchronize throws.

Mahdi Ben RhoumaMahdi Ben Rhouma
7 min read

TL;DR#

TypeORM throws DataTypeNotSupportedError on an array-typed property because a bare @Column() gives it no base Postgres type to build an array from. The fix is to pass both an explicit type and array: true: @Column("int", { array: true }). Reproduced on PostgreSQL 16 (official Docker image), TypeORM 0.3.20: the bare decorator fails every time, and only the type-plus-array:true form creates a working integer[] column.

plaintext
DataTypeNotSupportedError: Data type "Array" in "Home.kid_ages" is not supported by "postgres" database.

Why it happens#

TypeORM's @Column() decorator, used on its own, tries to infer a column type from the TypeScript type of the property via reflect-metadata. For most types this works: string becomes varchar, number becomes integer, boolean becomes boolean. For an array type such as number[] or string[], there is no single corresponding Postgres type to infer — "array of what, stored how" is not information TypeScript's reflection carries. Rather than guessing, TypeORM's schema builder raises DataTypeNotSupportedError and refuses to build the table.

This is exactly what the original Stack Overflow question hit on a kid_ages: Integer[] column: migrating threw Data type "Array" in "home.kid_ages" is not supported by "postgres" database. Reproducing the same entity shape against a throwaway PostgreSQL 16 container (official postgres:16-alpine image, TypeORM 0.3.20, synchronize: true) on 6 October 2026 gave the identical error name and message, with the table identifier's casing (Home.kid_ages here) following the entity class name (Home) used in the reproduction.

PostgreSQL itself has no problem with array columns — integer[], text[], jsonb[] are all native column types, and TypeORM's own column types documentation lists the array option specifically for Postgres. The error is not a database limitation; it is TypeORM refusing to synthesize a type it was not told.

The original question tried @Column({ array: true, default: [], nullable: false }) kid_ages: string; — array: true set, but no base type passed as the first argument — and still got the same error. Reproducing that exact shape against TypeORM 0.3.20 does not throw: synchronize: true succeeds and \d home shows kid_ages | character varying[]. TypeORM now falls back to inferring the base element type from the property's own TypeScript type (string → varchar) when array: true is present, even without an explicit type string — a behaviour difference from the version the question was asked against in 2019. The one case that still fails on 0.3.20 is the bare @Column() with no options at all, because there is no array: true to trigger that fallback and no base type to infer from.

It is also worth being precise about what fails and what does not. The schema builder only raises DataTypeNotSupportedError while it is deciding what SQL CREATE TABLE / ALTER TABLE statement to emit, during synchronize: true or during migration generation. Nothing about querying or inserting into an existing, correctly typed array column is unusual once it exists — the friction is entirely at the one moment TypeORM has to turn a TypeScript property into a column definition, and it only has that one moment because reflect-metadata erases the element type (it reports Array, never Array<number>).

Fix#

1. Give the column both a base type and array: true.

ts
@Entity()
class Home {
  @PrimaryGeneratedColumn()
  id: number;
 
  @Column("int", { array: true, default: [] })
  kid_ages: number[];
}

"int" is the Postgres base type for each element; array: true tells the schema builder to wrap it as int[]. For strings, use "text" instead:

ts
@Entity()
class Tag {
  @PrimaryGeneratedColumn()
  id: number;
 
  @Column("text", { array: true })
  labels: string[];
}

The same pattern works for any scalar Postgres type TypeORM supports as an array element — "text", "int", "varchar", "uuid", "timestamp" — the schema builder only needs the element type named once.

2. Run synchronize: true (or a migration) and confirm it against a real connection.

ts
const dataSource = new DataSource({
  type: "postgres",
  host: "127.0.0.1",
  port: 5432,
  username: "postgres",
  password: "postgres",
  database: "testdb",
  synchronize: true,
  entities: [Home],
});
 
await dataSource.initialize();

Reproducing this step against a throwaway postgres:16-alpine container produced, with the corrected entity from step 1:

plaintext
INIT OK
SAVED ROW: {"kid_ages":[5,7,9],"id":1}

synchronize: true is convenient for a reproduction like this one but is not a production migration strategy — on a real project, generate a migration (typeorm migration:generate) once the entity is correct and run that migration instead, so the array column is created through a reviewed, versioned SQL statement rather than an automatic diff recomputed on every boot.

3. Querying an array column uses ANY, not IN or =.

ts
const repo = dataSource.getRepository(Home);
const found = await repo
  .createQueryBuilder("home")
  .where(":age = ANY (home.kid_ages)", { age: 7 })
  .getMany();

This matched the row whose kid_ages contained 7, confirmed against the live container: ANY-QUERY RESULT: [{"id":1,"kid_ages":[5,7,9]}].

Verify the fix#

Inspect the column TypeORM actually created with \d in psql (see also PostgreSQL DESCRIBE TABLE: the psql \d equivalent for the Supabase/SQL-client version of this check):

plaintext
                              Table "public.home"
  Column  |   Type    | Collation | Nullable |             Default
----------+-----------+-----------+----------+----------------------------------
 id       | integer   |           | not null | nextval('home_id_seq'::regclass)
 kid_ages | integer[] |           | not null | '{}'::integer[]

integer[] confirms the array column exists; a plain integer row would mean the fix did not take — exactly the trap described below. It is worth checking the generated type rather than only checking that synchronize did not throw, because (as the next section shows) a wrong-but-valid column definition does not throw either.

A second, cheaper check that does not need psql is to read back a saved row and confirm the value round-trips as a real JavaScript array rather than, say, a comma-separated string:

ts
const row = await dataSource.getRepository(Home).findOneBy({ id: 1 });
console.log(Array.isArray(row.kid_ages), row.kid_ages);

Against the reproduction above this logged true [ 5, 7, 9 ] — pg's array-typed result parsing on a correctly declared array column hands back a native array, with no string-splitting on the application side needed.

One email a month — the latest explained errors

What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.

The trap that looks like a fix but isn't#

Adding only a base type, without array: true, is the variant worth checking for by hand — it does not reproduce the original error, so it looks like it worked. Reproduced the same entity with @Column("int") (no array option) against a freshly dropped table: synchronize: true completed with UNEXPECTED SUCCESS, and \d home showed a plain scalar kid_ages | integer column, not an array. Every subsequent .save() call that pushes an array value into that column will fail or silently coerce, not because Postgres rejects arrays, but because the column was never one. The option that matters is array: true itself, not the presence of a base type.

If the next error after fixing this one is a connection failure rather than a type error — for example the app and Postgres are both running in Docker Compose but the app still can't reach the database — that is usually a hostname issue, not a TypeORM issue: see Fix ECONNREFUSED 127.0.0.1:5432 in Docker Compose (Node). And if the next failure after inserting array rows is a duplicate-key error on a concurrent insert, Postgres INSERT If Not Exists: Fix Duplicate Key Violations covers the ON CONFLICT pattern for that case.

Arrays are a genuinely native PostgreSQL feature, and TypeORM supports them fully once the column is declared correctly — the error is a missing hint at entity-definition time, not a database limitation, and it is worth checking the generated column type directly rather than trusting that synchronize succeeding means the schema is what was intended.

Frequently Asked Questions

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote