lfnovo/open-notebook · warning · HTTPException

Model '{model_data.name}' already exists for provider '{mode

Error message

Model '{model_data.name}' already exists for provider '{model_data.provider}' with type '{model_data.type}'

What it means

400 raised by POST /api/v1/models when a model with the same name already exists for the same provider and type. The duplicate check runs a case-insensitive repo_query (name, provider, type all lowercased) before creating the Model.

Source

Thrown at api/routers/models.py:229

        if model_data.type not in valid_types:
            raise HTTPException(
                status_code=400,
                detail=f"Invalid model type. Must be one of: {valid_types}",
            )

        # Check for duplicate model name under the same provider and type (case-insensitive)
        from open_notebook.database.repository import repo_query

        existing = await repo_query(
            "SELECT * FROM model WHERE string::lowercase(provider) = $provider AND string::lowercase(name) = $name AND string::lowercase(type) = $type LIMIT 1",
            {
                "provider": model_data.provider.lower(),
                "name": model_data.name.lower(),
                "type": model_data.type.lower(),
            },
        )
        if existing:
            raise HTTPException(
                status_code=400,
                detail=f"Model '{model_data.name}' already exists for provider '{model_data.provider}' with type '{model_data.type}'",
            )

        new_model = Model(
            name=model_data.name,
            provider=model_data.provider,
            type=model_data.type,
            credential=model_data.credential,
        )
        await new_model.save()

        return ModelResponse(
            id=new_model.id or "",
            name=new_model.name,
            provider=new_model.provider,
            type=new_model.type,
            credential=new_model.credential,

View on GitHub (pinned to a7de90d38a)

Solutions

  1. Use a different model name or delete/update the existing model first (DELETE /models/{id})
  2. GET /models first and check for case-insensitive duplicates before creating
  3. Make client creation idempotent by keying on provider+name+type

Example fix

// before
POST /models {"name":"GPT-4o","provider":"openai","type":"language"}
// after
const existing = (await fetch('/api/v1/models').then(r=>r.json())).find(m=>m.name.toLowerCase()==='gpt-4o' && m.provider==='openai');
if (existing) { await fetch(`/api/v1/models/${existing.id}`, {method:'PUT', ...}); } else { /* POST */ }
Defensive patterns

Strategy: validation

Validate before calling

const models = await api.getModels();
const dup = models.find(m => m.name.toLowerCase()===data.name.toLowerCase() && m.provider===data.provider && m.type===data.type);
if (dup) { await api.updateModel(dup.id, data); return; }

Try / catch

try { await api.createModel(data); } catch (e) { if (e.status === 400 && /already exists/.test(e.detail)) { /* update instead or surface friendly message */ } }

Prevention

When it happens

Trigger: POST /models with a name that differs only by case from an existing entry (e.g. 'GPT-4o' vs 'gpt-4o') for the same provider+type; re-submitting a form after a first success.

Common situations: Double form submission, re-running a seed script, or retrying a POST that actually succeeded but the UI showed an error.

Related errors


AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27). Data as JSON: /api/errors/9c6fd8918293fe21. Report an issue: GitHub.