n8n-io/n8n · error · TypeORMError
Column "${virtualColumn.propertyName}" of Entity "${entityMe
Error message
Column "${virtualColumn.propertyName}" of Entity "${entityMetadata.name}" is defined as VIRTUAL, but Postgres supports only STORED generated columns. What it means
Thrown by EntityMetadataValidator.validate in the postgres-only branch when any column has asExpression set (i.e. it is a generated column) and generatedType is either unset or 'VIRTUAL'. Postgres supports only STORED generated columns, so a virtual generated column fails validation.
Source
Thrown at packages/@n8n/typeorm/src/metadata-builder/EntityMetadataValidator.ts:132
throw new DataTypeNotSupportedError(column, normalizedColumn, driver.options.type);
if (column.length && driver.withLengthColumnTypes.indexOf(normalizedColumn) === -1)
throw new TypeORMError(
`Column ${column.propertyName} of Entity ${entityMetadata.name} does not support length property.`,
);
if (column.type === 'enum' && !column.enum && !column.enumName)
throw new TypeORMError(
`Column "${column.propertyName}" of Entity "${entityMetadata.name}" is defined as enum, but missing "enum" or "enumName" properties.`,
);
});
// Postgres supports only STORED generated columns.
if (driver.options.type === 'postgres') {
const virtualColumn = entityMetadata.columns.find(
(column) =>
column.asExpression && (!column.generatedType || column.generatedType === 'VIRTUAL'),
);
if (virtualColumn)
throw new TypeORMError(
`Column "${virtualColumn.propertyName}" of Entity "${entityMetadata.name}" is defined as VIRTUAL, but Postgres supports only STORED generated columns.`,
);
}
// check if relations are all without initialized properties
const entityInstance = entityMetadata.create(undefined, {
fromDeserializer: true,
});
entityMetadata.relations.forEach((relation) => {
if (relation.isManyToMany || relation.isOneToMany) {
// we skip relations for which persistence is disabled since initialization in them cannot harm somehow
if (relation.persistenceEnabled === false) return;
// get entity relation value and check if its an array
const relationInitializedValue = relation.getEntityValue(entityInstance);
if (Array.isArray(relationInitializedValue)) throw new InitializedRelationError(relation);
}
});View on GitHub (pinned to 5ac6606e81)
Solutions
- Set generatedType: 'STORED' on the generated column when targeting postgres.
- If the column truly must be virtual, choose a different database (postgres will reject it).
- Drop the asExpression option if the column is not actually generated.
Example fix
// before
@Column({
type: 'generated',
asExpression: 'lower(name)',
generatedType: 'VIRTUAL',
})
nameLower: string;
// after (postgres)
@Column({
type: 'generated',
asExpression: 'lower(name)',
generatedType: 'STORED',
})
nameLower: string; Defensive patterns
Strategy: validation
Validate before calling
function assertNoVirtualGeneratedOnPostgres(dataSource: DataSource): string[] {
if (dataSource.options.type !== 'postgres') return [];
const problems: string[] = [];
for (const meta of dataSource.entityMetadatas) {
for (const col of meta.columns) {
if (col.asExpression && (!col.generatedType || col.generatedType === 'VIRTUAL')) {
problems.push(`${meta.targetName}.${col.propertyName} is VIRTUAL generated; postgres needs STORED`);
}
}
}
return problems;
} Try / catch
try {
await dataSource.initialize();
} catch (err) {
if (err.message.includes('VIRTUAL, but Postgres supports only STORED')) {
// change generatedType to 'STORED'
}
throw err;
} Prevention
- Default generatedType to 'STORED' on shared entities used across MySQL and postgres.
- Run the post-init generated-column validator in CI for the postgres target.
- Document that virtual generated columns are MySQL-only in the project's data-modelling guide.
When it happens
Trigger: Declaring a generated column with @Column({ type: 'generated', asExpression: '...', generatedType: 'VIRTUAL' }) (or omitting generatedType) on a postgres DataSource.
Common situations: Sharing entity definitions between MySQL (which supports VIRTUAL) and postgres; defaulting generatedType and assuming it stays STORED; copying a generated-column example written for MySQL.
Related errors
- Postgres package has not been found installed. Try to instal
- Invalid PgVectorStore table name "${String(this.tableName)}"
- Filter operator "${operator}" on key "${key}" requires a non
- Azure Blob container name not configured. Please set `N8N_EX
- External storage bucket name not configured. Please set `N8N
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/65860ab55d855181.
Report an issue: GitHub.