immich-app/immich · error

extension is not installed

Error message

${EXTENSION_NAMES[extension]} extension is not installed

What it means

updateVectorExtension upgrades a vector extension to targetVersion, but first checks the currently installed version via getExtensionVersions. If the extension is not installed in this database (installedVersion is empty), it throws instead of silently proceeding. Note this method updates an existing extension; installing a fresh one is a different code path.

Solutions

  1. Install the extension first: CREATE EXTENSION IF NOT EXISTS vector; (or 'vectors'/'vchord') then retry the update
  2. Confirm you are connected to the Immich database (check DB_DATABASE_NAME) — the extension may be installed in a different database
  3. Check installed extensions with SELECT extname FROM pg_extension; and use the exact name Immich expects
  4. If the extension was intentionally removed, use the install path (e.g. Immich's createVectorExtension) instead of the update path

Example fix

-- before running the app/upgrade
DROP EXTENSION IF EXISTS vector;
-- after
CREATE EXTENSION IF NOT EXISTS vector;
Defensive patterns

Strategy: validation

Validate before calling

const { rows } = await sql`SELECT extname FROM pg_extension WHERE extname IN ('vector','vectors','vchord')`;
if (!rows.some((r) => r.extname === 'vector')) await sql`CREATE EXTENSION IF NOT EXISTS vector`;

Type guard

function isInstalled(versions: { name: string; installedVersion: string | null }[]): boolean {
  return versions.length > 0 && versions[0].installedVersion != null;
}

Try / catch

try {
  await repo.updateVectorExtension(extension);
} catch (e) {
  if ((e as Error).message.endsWith('extension is not installed')) {
    await sql`CREATE EXTENSION IF NOT EXISTS vector`; // install then retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Calling updateVectorExtension('vector', ...) when the CREATE EXTENSION for that extension was never run in the connected database, or it was dropped (DROP EXTENSION) beforehand.

Common situations: Running migration/upgrade tooling against a fresh database that never had the extension; the extension was dropped during a failed migration; connecting to the wrong database/schema where the extension is absent; hand-running DROP EXTENSION during a manual cleanup.

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


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/283374380f475029. Report an issue: GitHub.

Appendix: source

Thrown at server/src/repositories/database.repository.ts:131

  async createExtension(extension: DatabaseExtension): Promise<void> {
    this.logger.log(`Creating ${EXTENSION_NAMES[extension]} extension`);
    await sql`CREATE EXTENSION IF NOT EXISTS ${sql.raw(extension)} CASCADE`.execute(this.db);
    if (extension === DatabaseExtension.VectorChord) {
      const dbName = sql.id(await this.getDatabaseName());
      await sql`ALTER DATABASE ${dbName} SET vchordrq.probes = 1`.execute(this.db);
      await sql`SET vchordrq.probes = 1`.execute(this.db);
    }
  }

  async dropExtension(extension: DatabaseExtension): Promise<void> {
    this.logger.log(`Dropping ${EXTENSION_NAMES[extension]} extension`);
    await sql`DROP EXTENSION IF EXISTS ${sql.raw(extension)}`.execute(this.db);
  }

  async updateVectorExtension(extension: VectorExtension, targetVersion?: string): Promise<void> {
    const [{ availableVersion, installedVersion }] = await this.getExtensionVersions([extension]);
    if (!installedVersion) {
      throw new Error(`${EXTENSION_NAMES[extension]} extension is not installed`);
    }

    if (!availableVersion) {
      throw new Error(`No available version for ${EXTENSION_NAMES[extension]} extension`);
    }
    targetVersion ??= availableVersion;

    if (!diff(installedVersion, targetVersion)) {
      return;
    }

    await Promise.all([
      this.db.schema.dropIndex(VectorIndex.Clip).ifExists().execute(),
      this.db.schema.dropIndex(VectorIndex.Face).ifExists().execute(),
    ]);

    await sql`ALTER EXTENSION ${sql.raw(extension)} UPDATE TO ${sql.lit(targetVersion)}`.execute(this.db);
    await Promise.all([this.reindexVectors(VectorIndex.Clip), this.reindexVectors(VectorIndex.Face)]);

View on GitHub (pinned to e55ac299a4)