HKUDS/DeepTutor · warning · ValueError

Unsupported import source: {value!r}

Error message

Unsupported import source: {value!r}

What it means

Pydantic ValidationError raised by the ChatHistoryImportRequest 'source' field validator when the supplied source is not in _ALLOWED_SOURCES after strip+lowercase normalization. FastAPI turns this into a 422 with the message in the errors array.

Source

Thrown at deeptutor/api/routers/imports.py:70

    updated_at: float
    messages: list[ImportedMessage] = Field(default_factory=list)


class ChatHistoryImportRequest(BaseModel):
    source: str = Field(..., min_length=1)
    # All sessions in one request belong to one agent (a named, scoped slice of
    # a source folder). Empty when the client hasn't adopted the agent model yet
    # — the backend stays backwards-compatible by simply omitting attribution.
    agent_id: str = Field(default="", max_length=256)
    agent_name: str = Field(default="", max_length=256)
    sessions: list[ImportedSession] = Field(default_factory=list)

    @field_validator("source")
    @classmethod
    def _normalize_source(cls, value: str) -> str:
        normalized = (value or "").strip().lower()
        if normalized not in _ALLOWED_SOURCES:
            raise ValueError(f"Unsupported import source: {value!r}")
        return normalized


@router.post("/chat-history")
async def import_chat_history(payload: ChatHistoryImportRequest) -> dict[str, Any]:
    if not payload.sessions:
        raise HTTPException(status_code=400, detail="No sessions to import")
    if len(payload.sessions) > _MAX_SESSIONS_PER_REQUEST:
        raise HTTPException(
            status_code=413,
            detail=f"Too many sessions in one request (max {_MAX_SESSIONS_PER_REQUEST})",
        )

    store = get_sqlite_session_store()
    imported = 0
    skipped = 0
    results: list[dict[str, Any]] = []

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Read the 422 response — it echoes the allowed values via the message
  2. Check _ALLOWED_SOURCES in deeptutor/api/routers/imports.py for the current set
  3. Send an allowed source string, lowercase (normalization handles case/whitespace, not synonyms)
  4. File a feature request if a legitimate source is unsupported

Example fix

// before
{"source": "Claude Desktop", "sessions": [...]}
// after
{"source": "claude", "sessions": [...]}
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {'claude', 'chatgpt', 'file'}  # mirror _ALLOWED_SOURCES
source = source.strip().lower()
assert source in ALLOWED, f'unsupported source {source!r}'

Type guard

def is_allowed_source(src: str, allowed: set[str]) -> bool:
    return (src or '').strip().lower() in allowed

Try / catch

try:
    client.post('/imports/chat-history', json=payload)
except httpx.HTTPStatusError as e:
    if e.response.status_code == 422:
        fix_source_field(e.response.json())  # validator message lists the bad value

Prevention

When it happens

Trigger: POST /imports/chat-history with a JSON body whose 'source' is e.g. 'claude ', 'CSV', 'notion', or missing-but-defaulted invalid — anything not in the allowed set.

Common situations: New export format not yet supported, typos in source name, or version change altering the allowed list.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/ee9bf9630836e317. Report an issue: GitHub.