{"record":{"id":"ee9bf9630836e317","repo":"HKUDS/DeepTutor","slug":"unsupported-import-source-value-r","errorCode":null,"errorMessage":"Unsupported import source: {value!r}","messagePattern":"Unsupported import source: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"warning","filePath":"deeptutor/api/routers/imports.py","lineNumber":70,"sourceCode":"    updated_at: float\n    messages: list[ImportedMessage] = Field(default_factory=list)\n\n\nclass ChatHistoryImportRequest(BaseModel):\n    source: str = Field(..., min_length=1)\n    # All sessions in one request belong to one agent (a named, scoped slice of\n    # a source folder). Empty when the client hasn't adopted the agent model yet\n    # — the backend stays backwards-compatible by simply omitting attribution.\n    agent_id: str = Field(default=\"\", max_length=256)\n    agent_name: str = Field(default=\"\", max_length=256)\n    sessions: list[ImportedSession] = Field(default_factory=list)\n\n    @field_validator(\"source\")\n    @classmethod\n    def _normalize_source(cls, value: str) -> str:\n        normalized = (value or \"\").strip().lower()\n        if normalized not in _ALLOWED_SOURCES:\n            raise ValueError(f\"Unsupported import source: {value!r}\")\n        return normalized\n\n\n@router.post(\"/chat-history\")\nasync def import_chat_history(payload: ChatHistoryImportRequest) -> dict[str, Any]:\n    if not payload.sessions:\n        raise HTTPException(status_code=400, detail=\"No sessions to import\")\n    if len(payload.sessions) > _MAX_SESSIONS_PER_REQUEST:\n        raise HTTPException(\n            status_code=413,\n            detail=f\"Too many sessions in one request (max {_MAX_SESSIONS_PER_REQUEST})\",\n        )\n\n    store = get_sqlite_session_store()\n    imported = 0\n    skipped = 0\n    results: list[dict[str, Any]] = []\n","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/api/routers/imports.py#L52-L88","documentation":"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.","triggerScenarios":"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.","commonSituations":"New export format not yet supported, typos in source name, or version change altering the allowed list.","solutions":["Read the 422 response — it echoes the allowed values via the message","Check _ALLOWED_SOURCES in deeptutor/api/routers/imports.py for the current set","Send an allowed source string, lowercase (normalization handles case/whitespace, not synonyms)","File a feature request if a legitimate source is unsupported"],"exampleFix":"// before\n{\"source\": \"Claude Desktop\", \"sessions\": [...]}\n// after\n{\"source\": \"claude\", \"sessions\": [...]}","handlingStrategy":"validation","validationCode":"ALLOWED = {'claude', 'chatgpt', 'file'}  # mirror _ALLOWED_SOURCES\nsource = source.strip().lower()\nassert source in ALLOWED, f'unsupported source {source!r}'","typeGuard":"def is_allowed_source(src: str, allowed: set[str]) -> bool:\n    return (src or '').strip().lower() in allowed","tryCatchPattern":"try:\n    client.post('/imports/chat-history', json=payload)\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 422:\n        fix_source_field(e.response.json())  # validator message lists the bad value","preventionTips":["Pin the allowed source list from imports.py at build time","Normalize case/whitespace client-side before sending"],"tags":["imports","pydantic","validation","http-422"],"backgroundTag":"schema-validation-failed","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}