dotCMS / dotCMS/core

fix(dotAI): EmbeddingContentListener runs CREATE EXTENSION vector on archive/delete even when dotAI is disabled

Open
#36,204 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dotCMS : AI OKR : Customer Support stale Team : Maintenance Type : Defect
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Problem Statement

When dotAI is not configured/disabled, the embeddings content listener still attempts to initialize the PostgreSQL vector extension on archive/unpublish/delete operations. On a standard PostgreSQL instance without pgvector, this throws:

org.postgresql.util.PSQLException: ERROR: extension "vector" is not available
  Detail: Could not open extension control file ".../vector.control": No such file or directory.
  Hint: The extension must first be installed on the system where PostgreSQL is running.

The connection is then marked broken (SQLSTATE 0A000). The operation itself (archive/unpublish/delete) still completes — only the embeddings cleanup fails — but it logs a stack trace intermittently and forces a pgvector requirement on customers who don't use AI at all.

Root cause (confirmed with @ivan.hoffmann):

  • EmbeddingsInitializer.init() subscribes EmbeddingContentListener unconditionally at startup — no AI-enabled gate, and no toggle to disable just this listener.
  • EmbeddingContentListener.deleteFromIndexes()'s only guard is a call to getConfigJson(host). But getConfigJson() wraps getAppConfig() in Try.of(...).getOrElse(new JSONObject()), so when dotAI is disabled the DotAIAppConfigDisabledException is swallowed and converted to an empty {}. deleteFromIndexes() does not check for the empty config and proceeds to call EmbeddingsAPIImpl.deleteEmbedding() regardless.
  • deleteEmbedding() triggers the lazy EmbeddingsFactory.impl singleton, whose constructor runs initVectorExtension()CREATE EXTENSION vector.
  • The publish/add path (addToIndexesIfNeeded) gets the same empty config but iterates over zero indexes → harmless no-op. Only the delete path fires the error, which is why it appears intermittently (archive/unpublish/delete only).
Steps to Reproduce
  1. Use a standard PostgreSQL DB without the pgvector extension installed.
  2. Leave dotAI unconfigured (no API key/URLs in Settings → Apps → dotAI).
  3. Archive, unpublish, or delete any contentlet.
  4. Observed: EmbeddingContentListener.onArchive → deleteFromIndexes → EmbeddingsAPIImpl.deleteEmbedding → EmbeddingsFactory.<init> → initVectorExtension → CREATE EXTENSION vector throws PSQLException: extension "vector" is not available; the pooled connection is marked broken.
Expected vs. Actual
  • Expected: With dotAI disabled, no embeddings/vector initialization runs. No pgvector requirement on the database.
  • Actual: The delete path attempts CREATE EXTENSION vector and errors on standard Postgres.
Acceptance Criteria
  • With dotAI disabled, archive/unpublish/delete does not attempt to initialize the vector extension or touch the embeddings layer.
  • No PSQLException: extension "vector" is not available is logged when dotAI is disabled.
  • No pgvector extension is required for normal content operations when AI is not in use.
  • Integration test covering archive/delete on a disabled-dotAI host.
Proposed Fix (per @ivan.hoffmann)

Either:

  • Have deleteFromIndexes() check for an empty config JSON and return early, or
  • Have getConfigJson() return an Optional instead of swallowing DotAIAppConfigDisabledException, and have callers short-circuit when absent.
Affected Version

25.07.10 LTS (present since the listener was registered in #28770 / 24.06.21). Please confirm whether a fix should be backported to the 25.07.x LTS line.

Proposed Priority

Priority 2 - Important

External Links

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with EmbeddingContentListener.deleteFromIndexes(), getConfigJson(), and EmbeddingsAPIImpl.deleteEmbedding(); trace how EmbeddingsFactory initializes the vector extension when dotAI is disabled. Run the relevant content archive, unpublish, or delete integration tests, then add coverage for a disabled-dotAI host and verify these operations do not touch embeddings or log a missing-vector error.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, postgresql
Domain
backend, databases, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.