{"record":{"id":"f726351238fadff1","repo":"lfnovo/open-notebook","slug":"item-type-must-be-either-source-or-note","errorCode":null,"errorMessage":"Item type must be either 'source' or 'note'","messagePattern":"Item type must be either 'source' or 'note'","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"api/routers/embedding.py","lineNumber":32,"sourceCode":"\n\n@router.post(\"/embed\", response_model=EmbedResponse)\nasync def embed_content(embed_request: EmbedRequest):\n    \"\"\"Embed content for vector search.\"\"\"\n    try:\n        # Check if embedding model is available\n        if not await model_manager.get_embedding_model():\n            raise HTTPException(\n                status_code=400,\n                detail=\"No embedding model configured. Please configure one in the Models section.\",\n            )\n\n        item_id = embed_request.item_id\n        item_type = embed_request.item_type.lower()\n\n        # Validate item type\n        if item_type not in [\"source\", \"note\"]:\n            raise HTTPException(\n                status_code=400, detail=\"Item type must be either 'source' or 'note'\"\n            )\n\n        # Branch based on processing mode\n        if embed_request.async_processing:\n            # ASYNC PATH: Submit command for background processing\n            logger.info(f\"Using async processing for {item_type} {item_id}\")\n\n            try:\n                # Import commands to ensure they're registered\n                import commands.embedding_commands  # noqa: F401\n\n                # Submit type-specific command\n                if item_type == \"source\":\n                    command_name = \"embed_source\"\n                    command_input = {\"source_id\": item_id}\n                else:  # note\n                    command_name = \"embed_note\"","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/api/routers/embedding.py#L14-L50","documentation":"400 from POST /api/embed rejecting an unsupported item_type. The endpoint only embeds two item kinds — 'source' and 'note' — and lowercases the input before checking, so anything else ('document', 'chat', 'Note ' with trailing junk, etc.) fails this validation.","triggerScenarios":"POST /api/embed with item_type: 'chunk', 'document', 'conversation', or a misspelled value like 'sourse'. The check happens after .lower(), so case is fine but the string must be exactly 'source' or 'note'.","commonSituations":"Client code written against a different API version or assumed vocabulary, typos in integrations/scripts, or passing a database table name instead of the item type.","solutions":["Change item_type to exactly 'source' or 'note' (case-insensitive)","Check the EmbedRequest schema/client SDK for the accepted enum values","If you need to embed a different entity type, find or create its dedicated endpoint instead"],"exampleFix":"// before\n{\"item_id\": \"...\", \"item_type\": \"document\", \"async_processing\": true}\n// after\n{\"item_id\": \"...\", \"item_type\": \"source\", \"async_processing\": true}","handlingStrategy":"type-guard","validationCode":"const ITEM_TYPES = ['source', 'note'];\nif (!ITEM_TYPES.includes(embedRequest.item_type.toLowerCase())) throw new Error(`item_type must be one of ${ITEM_TYPES}`);","typeGuard":"function isEmbedItemType(v: string): v is 'source' | 'note' {\n  return v === 'source' || v === 'note';\n}","tryCatchPattern":"try {\n  await api.embed(payload);\n} catch (e) {\n  if (e.status === 400 && /item type/i.test(e.detail)) fixItemType(payload);\n  throw e;\n}","preventionTips":["Encode the 'source'|'note' union in client types so invalid values fail at compile time","Validate against the enum before calling the API"],"tags":["embedding","validation","http-400"],"backgroundTag":"request-validation-failed","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}