{"record":{"id":"36a2e4558ce2fdb8","repo":"lfnovo/open-notebook","slug":"source-input-data-source-id-has-no-text-to-emb","errorCode":null,"errorMessage":"Source '{input_data.source_id}' has no text to embed","messagePattern":"Source '(.+?)' has no text to embed","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"commands/embedding_commands.py","lineNumber":332,"sourceCode":"    3. Detect content type from file path or content\n    4. Chunk text using appropriate splitter\n    5. Generate embeddings for all chunks in batches\n    6. Bulk INSERT source_embedding records\n\n    Retry Strategy:\n    - Retries up to 5 times for transient failures (network, timeout, etc.)\n    - Uses exponential-jitter backoff (1-60s)\n    - Does NOT retry permanent failures (ValueError for validation errors)\n    \"\"\"\n\n    async def embed() -> Tuple[Dict[str, Any], str]:\n        # 1. Load source\n        source = await Source.get(input_data.source_id)\n        if not source:\n            raise ValueError(f\"Source '{input_data.source_id}' not found\")\n\n        if not source.full_text or not source.full_text.strip():\n            raise ValueError(f\"Source '{input_data.source_id}' has no text to embed\")\n\n        # 2. DELETE existing embeddings (idempotency)\n        logger.debug(f\"Deleting existing embeddings for source {input_data.source_id}\")\n        await repo_query(\n            \"DELETE source_embedding WHERE source = $source_id\",\n            {\"source_id\": ensure_record_id(input_data.source_id)},\n        )\n\n        # 3. Detect content type from file path if available\n        file_path = source.asset.file_path if source.asset else None\n        content_type = detect_content_type(source.full_text, file_path)\n        logger.debug(f\"Detected content type: {content_type.value}\")\n\n        # 4. Chunk text using appropriate splitter\n        chunks = chunk_text(source.full_text, content_type=content_type)\n        total_chunks = len(chunks)\n\n        # Log chunk statistics for debugging","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/lfnovo/open-notebook/blob/a7de90d38aaf18ee85fd661854d35c11e44613e2/commands/embedding_commands.py#L314-L350","documentation":"ValueError raised by the source embed command when the source exists but full_text is empty or whitespace-only. Sources must have completed text extraction before embedding; this guard fails before deleting/re-creating embeddings.","triggerScenarios":"embed_source runs on a source whose content fetch or transcription did not finish (or failed silently), leaving full_text empty — e.g. a podcast source where transcription never completed.","commonSituations":"See trigger scenarios.","solutions":["Re-open the source and let ingestion/text-extraction complete, then re-run embedding","Check the worker (make worker-start) is running so ingestion jobs actually process","Inspect the source record in SurrealDB to confirm full_text is populated"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"source = await Source.get(source_id)\nif not source.full_text or not source.full_text.strip():\n    raise RuntimeError(f'source {source_id} ingestion incomplete — full_text empty')","typeGuard":"def source_ready_for_embedding(source) -> bool:\n    return bool(source and source.full_text and source.full_text.strip())","tryCatchPattern":null,"preventionTips":["Wait for ingestion/transcription to complete before embedding","Keep the async worker running so ingestion jobs finish","Check full_text on the source record when embedding fails"],"tags":["python","valueerror","embedding","source","empty-content","ingestion"],"backgroundTag":"empty-input-validation","analyzedSha":"a7de90d38aaf18ee85fd661854d35c11e44613e2","analyzedAt":"2026-08-27T02:39:58.166Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}