lfnovo/open-notebook · warning · HTTPException

Invalid model type. Must be one of: {valid_types}

Error message

Invalid model type. Must be one of: {valid_types}

What it means

400 raised by POST /api/v1/models when model_data.type is not one of the four allowed values: language, embedding, text_to_speech, speech_to_text. The type field is validated inline in the router before any duplicate check or DB write.

Source

Thrown at api/routers/models.py:212

            for model in models
        ]
    except HTTPException:
        raise
    except OpenNotebookError:
        raise
    except Exception as e:
        logger.error(f"Error fetching models: {str(e)}")
        raise HTTPException(status_code=500, detail=f"Error fetching models: {str(e)}")


@router.post("/models", response_model=ModelResponse)
async def create_model(model_data: ModelCreate):
    """Create a new model configuration."""
    try:
        # Validate model type
        valid_types = ["language", "embedding", "text_to_speech", "speech_to_text"]
        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,

View on GitHub (pinned to a7de90d38a)

Solutions

  1. Set type to exactly one of language, embedding, text_to_speech, speech_to_text (lowercase)
  2. Validate/normalize the type in the client before POSTing
  3. Update stale scripts or SDK helpers that use old type names

Example fix

// before
{"name":"gpt-4o","provider":"openai","type":"chat"}
// after
{"name":"gpt-4o","provider":"openai","type":"language"}
Defensive patterns

Strategy: validation

Validate before calling

const VALID = ['language','embedding','text_to_speech','speech_to_text'];
if (!VALID.includes(model.type)) throw new Error(`type must be one of ${VALID.join(', ')}`);

Type guard

const isModelType = (t: string): t is 'language'|'embedding'|'text_to_speech'|'speech_to_text' =>
  ['language','embedding','text_to_speech','speech_to_text'].includes(t);

Try / catch

try { await api.createModel(data); } catch (e) { if (e.status === 400 && /Invalid model type/.test(e.detail)) fixType(); }

Prevention

When it happens

Trigger: POST /models with body type: 'chat', 'llm', 'tts', 'transcription', or any casing/typo variant; the comparison is case-sensitive so 'Language' also fails.

Common situations: Frontend sending free-text or legacy type names; scripts written against older API versions; casing mismatches.

Related errors


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