invoke-ai/InvokeAI · error · ValueError
key does not match new_config.key
Error message
key does not match new_config.key
What it means
replace_model() overwrites an existing model record wholesale with new_config. To prevent accidentally re-keying a record, it requires that the `key` argument equals new_config.key; a mismatch means the caller is trying to replace one record with a config belonging to a different model, so ValueError is raised before any DB write.
Source
Thrown at invokeai/app/services/model_records/model_records_sql.py:227
json_serialized = record.model_dump_json()
cursor.execute(
"""--sql
UPDATE models
SET
config=?
WHERE id=?;
""",
(json_serialized, key),
)
if cursor.rowcount == 0:
raise UnknownModelException("model not found")
return self.get_model(key)
def replace_model(self, key: str, new_config: AnyModelConfig) -> AnyModelConfig:
if key != new_config.key:
raise ValueError("key does not match new_config.key")
with self._db.transaction() as cursor:
cursor.execute(
"""--sql
UPDATE models
SET
config=?
WHERE id=?;
""",
(new_config.model_dump_json(), key),
)
if cursor.rowcount == 0:
raise UnknownModelException("model not found")
return self.get_model(key)
def get_model(self, key: str) -> AnyModelConfig:
"""
Retrieve the ModelConfigBase instance for the indicated model.
View on GitHub (pinned to 0b6a024f2f)
Solutions
- Set new_config.key = key (or build the new config from the fetched one so `.key` is preserved) before calling replace_model.
- Fetch the current config with get_model(key), copy() it, modify fields, and pass the copy back — its key stays consistent.
- If you actually want a new model entry, use add_model(new_config) instead of replace_model.
- Add an assertion comparing key == new_config.key early in your editing code to catch drift.
Example fix
// before
new_config = model_config.copy(update={"name": "new-name"})
records.replace_model(other_key, new_config)
// after
new_config = model_config.copy(update={"name": "new-name", "key": other_key})
records.replace_model(other_key, new_config) Defensive patterns
Strategy: validation
Validate before calling
assert key == new_config.key, f"replace_model: {key!r} != {new_config.key!r}"
records.replace_model(key, new_config) Type guard
def is_consistent_replace(key: str, cfg) -> bool:
return getattr(cfg, "key", None) == key Try / catch
try:
records.replace_model(key, new_config)
except ValueError as e:
if "key does not match" in str(e):
new_config.key = key
records.replace_model(key, new_config) Prevention
- Build replacement configs via copy(update=...) from the fetched record so .key is preserved
- Never hand-construct configs for replace_model; fetch-then-modify
- Add an assertion comparing keys before calling
- Use add_model when you actually want a new record
When it happens
Trigger: Calling replace_model(existing_key, new_config) where new_config.key differs from existing_key — e.g. constructing a modified copy of a config without copying its `.key`, or passing a config loaded for a different model.
Common situations: Copying a model config object and editing fields but not preserving `.key`; passing a freshly built config (key None/default) instead of a fetched one; mixing up configs when batch-editing many models.
Related errors
- Cannot relate a model to itself.
- Model '{model_key}' is not a TextLLM model (got {model_confi
- Model '{body.model_key}' not found
- Model '{model_key}' is not a LLaVA OneVision model (got {mod
- Unknown lora: {lora.lora.key}!
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/c24b01ec5ec043df.
Report an issue: GitHub.