chroma-core/chroma · error · Error

The task type cannot be changed after initialization.

Error message

The task type cannot be changed after initialization.

What it means

Intended to block changing the Gemini taskType (RETRIEVAL_DOCUMENT vs RETREVAL_QUERY etc.) after initialization, for the same reason as the model lock: task type shapes the vectors. Caveat grounded in the source: it compares oldConfig.taskType/newConfig.taskType (camelCase) while getConfig() emits task_type (snake_case), so for configs round-tripped through getConfig() both sides are undefined and this error can never fire; it only triggers when callers supply records carrying a literal camelCase taskType key.

Source

Thrown at clients/js/packages/chromadb-core/src/embeddings/GoogleGeminiEmbeddingFunction.ts:125

  getConfig(): StoredConfig {
    return {
      api_key_env_var: this.api_key_env_var,
      model_name: this.model,
      task_type: this.taskType,
    };
  }

  validateConfigUpdate(
    oldConfig: Record<string, any>,
    newConfig: Record<string, any>,
  ): void {
    if (oldConfig.model_name !== newConfig.model_name) {
      throw new Error("The model name cannot be changed after initialization.");
    }

    if (oldConfig.taskType !== newConfig.taskType) {
      throw new Error("The task type cannot be changed after initialization.");
    }
  }

  validateConfig(config: Record<string, any>): void {
    validateConfigSchema(config, "google_generative_ai");
  }
}

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Keep the task type identical across updates — fetch the stored config and reuse the field
  2. Create a new collection if the task type genuinely must change
  3. Library-level fix: compare oldConfig.task_type !== newConfig.task_type so the guard matches the keys getConfig() actually emits

Example fix

// before (library code): key never matches getConfig() output
if (oldConfig.taskType !== newConfig.taskType) {
  throw new Error("The task type cannot be changed after initialization.");
}

// after: compare the key the config actually stores
if (oldConfig.task_type !== newConfig.task_type) {
  throw new Error("The task type cannot be changed after initialization.");
}
Defensive patterns

Strategy: validation

Validate before calling

function taskTypeOf(cfg: Record<string, any>): unknown {
  return cfg.taskType ?? cfg.task_type; // cover both spellings: the guard reads camelCase, getConfig() emits snake_case
}
function assertSameTaskType(oldCfg: Record<string, any>, newCfg: Record<string, any>): void {
  if (taskTypeOf(oldCfg) !== taskTypeOf(newCfg)) {
    throw new Error("task type is immutable; create a new collection instead");
  }
}

Try / catch

try {
  await applyConfigUpdate(collection, newCfg);
} catch (e) {
  if (e instanceof Error && /task type cannot be changed/i.test(e.message)) {
    // task type differs: create a new collection; retrying will not help
  }
  throw e;
}

Prevention

When it happens

Trigger: validateConfigUpdate() invoked with old/new records that both contain a camelCase taskType key with different values — e.g. a wrapper that camelCases config keys before calling update, or hand-built config objects.

Common situations: Wrapper layers normalizing snake_case API config into camelCase; example code constructing config objects manually; otherwise usually seen as a silent no-op with getConfig()-produced configs, which is itself a bug worth reporting.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/385f3125a5458847. Report an issue: GitHub.