immich-app/immich · critical
No vector extension found. Available extensions
Error message
No vector extension found. Available extensions: ${VECTOR_EXTENSIONS.join(', ')} What it means
Immich requires a Postgres vector extension to run vector searches. On startup, getVectorExtension queries pg_available_extensions for the supported candidates (pgvector, pgvecto.rs, VectorChord) and throws if none is installed or available in the Postgres instance. This is a hard startup blocker: without an extension, CLIP/smart-search embeddings cannot be stored or queried.
Solutions
- Use the Immich-provided Postgres container image (which ships pgvector/VectorChord) instead of a stock Postgres image
- Install a supported extension on the server: CREATE EXTENSION vector; (or vectors/pg_vectorchord after installing its files) and restart Immich
- Verify availability first: SELECT name FROM pg_available_extensions WHERE name IN ('vector','vectors','pg_vectorchord'); — if empty, the Postgres installation lacks the extension binaries, so install them at the OS/image level
- If you intentionally switched extensions, ensure the old extension's data was migrated before removing it from the server
Example fix
// before (docker-compose.yml) image: postgres:16 // after image: ghcr.io/immich-app/postgres:14-vectorchord0.3.0-pgvectors0.2.0
Defensive patterns
Strategy: validation
Validate before calling
const { rows } = await sql`SELECT name FROM pg_available_extensions WHERE name IN ('vector','vectors','pg_vectorchord')`.execute(runner);
if (rows.length === 0) throw new Error('Postgres image lacks a vector extension; use the immich postgres image'); Type guard
function hasVectorExtension(available: { name: string }[]): boolean {
return ['vector', 'vectors', 'pg_vectorchord'].some((ext) => available.some((r) => r.name === ext));
} Try / catch
try {
await repo.getVectorExtension();
} catch (e) {
if ((e as Error).message.startsWith('No vector extension found')) {
// fall back to immich postgres image or install pgvector before proceeding
}
throw e;
} Prevention
- Always use the Immich-provided Postgres image with bundled vector extensions
- Smoke-test pg_available_extensions in CI before deploying
- Document extension requirements for custom Postgres setups
When it happens
Trigger: First startup against a Postgres server (including the bundled one) where none of the supported vector extensions exist in pg_available_extensions, e.g. the extensions were dropped from the image or an unsupported Postgres build is used.
Common situations: Using a plain 'postgres' Docker image instead of the Immich-provided image with pgvector/VectorChord preinstalled; running a custom Postgres build without contrib modules; upgrading Immich to a version that dropped support for a previously used extension; pg_available_extensions missing the entry because the extension files are not on the Postgres server's filesystem.
Understand the failure class
Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.
Related errors
- Column 'embedding' does not exist in table
- Could not retrieve dimension size of column
- 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/bbfb304fd7f38117.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/repositories/database.repository.ts:49
import { vectorIndexQuery } from 'src/utils/database.js';
export let cachedVectorExtension: VectorExtension | undefined;
export async function getVectorExtension(runner: Kysely<DB>): Promise<VectorExtension> {
if (cachedVectorExtension) {
return cachedVectorExtension;
}
cachedVectorExtension = new ConfigRepository().getEnv().database.vectorExtension;
if (cachedVectorExtension) {
return cachedVectorExtension;
}
const query = `SELECT name FROM pg_available_extensions WHERE name IN (${VECTOR_EXTENSIONS.map((ext) => `'${ext}'`).join(', ')})`;
const { rows: availableExtensions } = await sql.raw<{ name: VectorExtension }>(query).execute(runner);
const extensionNames = new Set(availableExtensions.map((row) => row.name));
cachedVectorExtension = VECTOR_EXTENSIONS.find((ext) => extensionNames.has(ext));
if (!cachedVectorExtension) {
throw new Error(`No vector extension found. Available extensions: ${VECTOR_EXTENSIONS.join(', ')}`);
}
return cachedVectorExtension;
}
export const probes: Record<VectorIndex, number> = {
[VectorIndex.Clip]: 1,
[VectorIndex.Face]: 1,
};
@Injectable()
export class DatabaseRepository {
private readonly asyncLock = new AsyncLock();
constructor(
@InjectKysely() private db: Kysely<DB>,
private logger: LoggingRepository,
private configRepository: ConfigRepository,
) {View on GitHub (pinned to e55ac299a4)