{"record":{"id":"4557a86d144efa0a","repo":"khoj-ai/khoj","slug":"invalid-conversation-log-format-str-e","errorCode":null,"errorMessage":"Invalid conversation_log format: {str(e)}","messagePattern":"Invalid conversation_log format: (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/khoj/database/models/__init__.py","lineNumber":679,"sourceCode":"    client = models.ForeignKey(ClientApplication, on_delete=models.CASCADE, default=None, null=True, blank=True)\n\n    # Slug is an app-generated conversation identifier. Need not be unique. Used as display title essentially.\n    slug = models.CharField(max_length=200, default=None, null=True, blank=True)\n\n    # The title field is explicitly set by the user.\n    title = models.CharField(max_length=500, default=None, null=True, blank=True)\n    agent = models.ForeignKey(Agent, on_delete=models.SET_NULL, default=None, null=True, blank=True)\n    file_filters = models.JSONField(default=list)\n    id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, primary_key=True, db_index=True)\n\n    def clean(self):\n        # Validate conversation_log structure\n        try:\n            messages = self.conversation_log.get(\"chat\", [])\n            for msg in messages:\n                ChatMessageModel.model_validate(msg)\n        except Exception as e:\n            raise ValidationError(f\"Invalid conversation_log format: {str(e)}\")\n\n    def save(self, *args, **kwargs):\n        self.clean()\n        super().save(*args, **kwargs)\n\n    @property\n    def messages(self) -> List[ChatMessageModel]:\n        \"\"\"Type-hinted accessor for conversation messages\"\"\"\n        validated_messages = []\n        for msg in self.conversation_log.get(\"chat\", []):\n            try:\n                # Clean up inferred queries if they contain None\n                if msg.get(\"intent\") and msg[\"intent\"].get(\"inferred_queries\"):\n                    msg[\"intent\"][\"inferred-queries\"] = [\n                        q for q in msg[\"intent\"][\"inferred_queries\"] if q is not None and isinstance(q, str)\n                    ]\n                msg[\"message\"] = str(msg.get(\"message\", \"\"))\n                validated_messages.append(ChatMessageModel.model_validate(msg))","sourceCodeStart":661,"sourceCodeEnd":697,"githubUrl":"https://github.com/khoj-ai/khoj/blob/ae229ca894c0b80ad84664afcfdde523b5e87057/src/khoj/database/models/__init__.py#L661-L697","documentation":"Entry.save() calls clean(), which validates that conversation_log['chat'] is a list of pydantic ChatMessageModel objects. Any exception during .get('chat') or model_validate is re-raised as a Django ValidationError.","triggerScenarios":"Saving a Chat model object whose conversation_log lacks a 'chat' key, has non-list 'chat', or contains message dicts missing/typoing required ChatMessageModel fields (e.g. 'message' key absent).","commonSituations":"Hand-built or migrated conversation_log dicts; schema drift after upgrading khoj's ChatMessageModel; third-party code writing raw dicts with wrong keys.","solutions":["Inspect str(e) in the message to see which field failed pydantic validation.","Build conversation_log entries with ChatMessageModel.model_validate(msg) (or construct model instances and use .model_dump()) before assigning.","If migrating old data, write a normalization step mapping legacy keys to the current schema."],"exampleFix":"# before\nchat.conversation_log = {\"chat\": [{\"role\": \"user\", \"text\": \"hi\"}]}  # wrong keys\nchat.save()  # ValidationError\n\n# after\nfrom khoj.database.models import ChatMessage\nmsg = ChatMessage(role=\"user\", content=\"hi\")  # pydantic model\nchat.conversation_log = {\"chat\": [msg.model_dump()]}\nchat.save()","handlingStrategy":"validation","validationCode":"from pydantic import ValidationError as PydanticError\nfrom khoj.database.models import ChatMessage\n\ndef valid_conversation_log(log: dict) -> bool:\n    try:\n        for msg in log.get(\"chat\", []):\n            ChatMessage.model_validate(msg)\n        return True\n    except PydanticError:\n        return False\n\nif not valid_conversation_log(chat.conversation_log):\n    raise HTTPException(400, \"bad conversation_log\")\nchat.save()","typeGuard":"def is_valid_conversation_log(log) -> bool:\n    return isinstance(log, dict) and isinstance(log.get(\"chat\"), list)","tryCatchPattern":"from django.core.exceptions import ValidationError\ntry:\n    chat.save()\nexcept ValidationError as e:\n    if \"Invalid conversation_log format\" in str(e):\n        # log details, quarantine row, or normalize messages\n        ...\n    raise","preventionTips":["Always construct chat messages via the pydantic ChatMessage model and .model_dump().","Never hand-write conversation_log dicts; reuse message objects from the chat pipeline.","Add a normalization step when migrating data between khoj versions."],"tags":["khoj","django","pydantic","validation","chat","schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"ae229ca894c0b80ad84664afcfdde523b5e87057","analyzedAt":"2026-08-27T03:32:41.843Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}