{"record":{"id":"223320e358235a7c","repo":"HKUDS/DeepTutor","slug":"no-sessions-to-import","errorCode":null,"errorMessage":"No sessions to import","messagePattern":"No sessions to import","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"deeptutor/api/routers/imports.py","lineNumber":77,"sourceCode":"    # 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\n    for incoming in payload.sessions:\n        # Drop content-less rows (e.g. tool-only turns the adapter could not\n        # reduce to text) so the transcript stays a clean human conversation.\n        messages = [m for m in incoming.messages if (m.content or \"\").strip()]\n        if not messages:\n            skipped += 1\n            results.append(","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/api/routers/imports.py#L59-L95","documentation":"400 raised by POST /imports/chat-history when payload.sessions is empty (falsy). The endpoint refuses no-op imports up front rather than returning a misleading success with imported=0.","triggerScenarios":"POSTing {\"source\":\"claude\",\"sessions\":[]} or omitting the sessions field if it defaults to empty.","commonSituations":"Upstream export produced zero sessions (filter bug, wrong file parsed) and the client forwards it blindly; test asserting rejection of empty bodies.","solutions":["Client-side: skip the import call when sessions is empty","Fix the upstream export/parse step that produced an empty list","If you genuinely expected sessions, verify the export file was read correctly"],"exampleFix":"// before\nawait client.post('/imports/chat-history', json={'source':'claude','sessions':sessions})\n// after\nif sessions:\n    await client.post('/imports/chat-history', json={'source':'claude','sessions':sessions})","handlingStrategy":"validation","validationCode":"if not sessions:\n    return  # nothing to import — skip the call entirely","typeGuard":"def has_sessions(payload: dict) -> bool:\n    return bool(payload.get('sessions'))","tryCatchPattern":"resp = client.post('/imports/chat-history', json=payload)\nif resp.status_code == 400 and 'No sessions' in resp.text:\n    return {'imported': 0}","preventionTips":["Guard the call with `if sessions:`","Log when an export yields zero sessions — it usually means an upstream parse bug"],"tags":["imports","http-400","empty-payload"],"backgroundTag":"empty-request-body","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}