immich-app/immich · warning
Column 'embedding' does not exist in table
Error message
Column 'embedding' does not exist in table '${table}', truncating and adding column. What it means
While reindexing a vector table, if the expected 'embedding' column is missing, the code truncates the table and recreates the column as real[]. All existing embeddings in that table are destroyed. This indicates schema drift: the table exists but its embedding column does not.
Solutions
- Re-run Immich so migrations complete, then trigger reindexing via the admin settings (vector extension update)
- Back up the database before reindexing since embeddings will be re-generated (smart search / facial recognition will rebuild)
- If the data is expendable, accept the TRUNCATE and let Immich re-embed the library
- Check migration history (migrations table) for failed migrations and fix them before reindexing
Example fix
// before (partial schema) ALTER TABLE smart_search ADD COLUMN embedding vector(512); // applied without full migration // after (let migrations + reindex rebuild) -- ensure migrations succeeded, then in Immich admin: Update vector extension, which drops/recreates the index correctly
Defensive patterns
Strategy: fallback
Validate before calling
const hasEmbedding = await db
.selectFrom('information_schema.columns')
.where('table_name', '=', table)
.where('column_name', '=', 'embedding')
.select(({ fn }) => fn.countAll().as('n'))
.executeTakeFirst();
if (!hasEmbedding || Number(hasEmbedding.n) === 0) {
throw new Error(`Refusing reindex: ${table} lacks 'embedding' column — run migrations first`);
} Try / catch
try {
await backupDatabase();
await db.reindexVectorsIfNeeded();
} catch (err) {
logger.error('Reindex failed; restore from backup if schema is inconsistent');
throw err;
} Prevention
- Back up the database (esp. embeddings) before upgrading Immich
- Never edit embedding columns manually; let migrations manage them
- Ensure previous migrations succeeded before triggering reindex
- Monitor migration logs after every version upgrade
When it happens
Trigger: reindexVectors (via updateVectorExtension or reindexVectorsIfNeeded) runs against a table where every information_schema row shows no column named 'embedding' — e.g. a partially applied migration or an old schema.
Common situations: Interrupted or failed migrations leaving the table without the embedding column; manual schema edits; restoring a partial dump; switching vector extensions against a legacy table layout.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Could not retrieve dimension size of column
- Table does not exist, skipping reindexing. This is only…
- extension is not installed
- Migration " " failed
- No available version for
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/40f354dbf7d2145e.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/repositories/database.repository.ts:235
const { rows } = await sql<{
columnName: string;
}>`SELECT column_name as "columnName" FROM information_schema.columns WHERE table_name = ${table}`.execute(this.db);
if (rows.length === 0) {
this.logger.warn(
`Table ${table} does not exist, skipping reindexing. This is only normal if this is a new Immich instance.`,
);
return;
}
const dimSize = await this.getDimensionSize(table);
lists ||= this.targetListCount(await this.getRowCount(table));
await this.db.transaction().execute(async (tx) => {
await sql`DROP INDEX IF EXISTS ${sql.raw(indexName)}`.execute(tx);
if (table === 'smart_search') {
await sql`ALTER TABLE ${sql.raw(table)} DROP CONSTRAINT IF EXISTS dim_size_constraint`.execute(tx);
}
if (rows.every((row) => row.columnName !== 'embedding')) {
this.logger.warn(`Column 'embedding' does not exist in table '${table}', truncating and adding column.`);
await sql`TRUNCATE TABLE ${sql.raw(table)}`.execute(tx);
await sql`ALTER TABLE ${sql.raw(table)} ADD COLUMN embedding real[] NOT NULL`.execute(tx);
}
await sql`ALTER TABLE ${sql.raw(table)} ALTER COLUMN embedding SET DATA TYPE real[]`.execute(tx);
await sql`
ALTER TABLE ${sql.raw(table)}
ALTER COLUMN embedding
SET DATA TYPE vector(${sql.raw(String(dimSize))})`.execute(tx);
await sql.raw(vectorIndexQuery({ vectorExtension, table, indexName, lists })).execute(tx);
});
this.logger.log(`Reindexed ${indexName}`);
void this.vacuum({ table }).catch((error) => this.logger.warn(`Failed to vacuum ${table}: ${error}`));
}
async vacuum({ analyze = false, table }: { analyze?: boolean; table?: keyof DB } = {}): Promise<void> {
try {
await sql`VACUUM ${sql.raw(analyze ? 'ANALYZE' : '')} ${sql.raw(table ?? '')}`.execute(this.db);
} catch (error) {View on GitHub (pinned to e55ac299a4)