{"record":{"id":"dca1e4034581d068","repo":"iflytek/astron-agent","slug":"ragflow-chunk-snapshot-remained-incomplete-after-retries-doc","errorCode":null,"errorMessage":"RAGFlow chunk snapshot remained incomplete after retries: doc={doc_id}, visible={last_visible_count}, expected={expected_count}","messagePattern":"RAGFlow chunk snapshot remained incomplete after retries: doc=(.+?), visible=(.+?), expected=(.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"core/knowledge/infra/ragflow/ragflow_utils.py","lineNumber":322,"sourceCode":"            expected_count = int(raw_count) if raw_count is not None else None\n        except (TypeError, ValueError):\n            return None\n        if expected_count is not None and expected_count < 0:\n            return None\n        return expected_count\n\n    @staticmethod\n    def _finalize_chunk_retrieval(\n        dataset_id: str,\n        doc_id: str,\n        *,\n        expected_count: Optional[int],\n        last_visible_count: int,\n        max_retries: int,\n    ) -> List[Dict[str, Any]]:\n        \"\"\"Return an empty snapshot or raise the final incomplete-state error.\"\"\"\n        if expected_count is not None and expected_count > last_visible_count:\n            raise RuntimeError(\n                \"RAGFlow chunk snapshot remained incomplete after retries: \"\n                f\"doc={doc_id}, visible={last_visible_count}, \"\n                f\"expected={expected_count}\"\n            )\n\n        if last_visible_count > 0:\n            raise RuntimeError(\n                \"RAGFlow chunk snapshot did not stabilize after retries: \"\n                f\"doc={doc_id}, visible={last_visible_count}, \"\n                f\"expected={expected_count}\"\n            )\n\n        logger.warning(\n            \"RAGFlow document returned zero chunks after retries: \"\n            \"dataset=%s doc=%s attempts=%d\",\n            dataset_id,\n            doc_id,\n            max_retries + 1,","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/knowledge/infra/ragflow/ragflow_utils.py#L304-L340","documentation":"_finalize_chunk_retrieval raises this RuntimeError when, after exhausting max_retries polling RAGFlow, the number of visible chunks (last_visible_count) is still strictly less than the expected_count derived from the document's chunk_count. The document's chunk index is not yet fully committed/visible in RAGFlow, so a complete snapshot cannot be returned.","triggerScenarios":"get_document_chunks is called immediately after a document finishes parsing while RAGFlow is still flushing chunks; expected_count > 0 and all polling attempts time out with fewer chunks visible.","commonSituations":"Right-after-upload race in bulk ingestion pipelines; RAGFlow indexing is slow under load; a small max_retries / short retry_delay budget in get_document_chunks; parse status reported done but index lagging.","solutions":["Increase max_retries or retry_delay in the get_document_chunks call to give RAGFlow more time","Verify the document's parse/run status in RAGFlow is actually complete before fetching chunks","Re-run chunk retrieval later once indexing has settled (retry with backoff)","Check RAGFlow service health/logs for indexing backlogs or failures"],"exampleFix":"// before\nchunks = await get_document_chunks(dataset_id, doc_id, max_retries=2, retry_delay=1.0)\n// after\nchunks = await get_document_chunks(dataset_id, doc_id, max_retries=10, retry_delay=3.0)","handlingStrategy":"retry","validationCode":"doc = await get_document_info(dataset_id, doc_id)\nif doc is None:\n    raise RuntimeError(f\"doc {doc_id} not found\")\nexpected = doc.get(\"chunk_count\") or 0\nif expected == 0:\n    raise RuntimeError(f\"doc {doc_id} has no chunks to wait for\")","typeGuard":null,"tryCatchPattern":"try:\n    chunks = await get_document_chunks(ds, doc_id, max_retries=10, retry_delay=3.0)\nexcept RuntimeError as e:\n    if \"remained incomplete\" in str(e):\n        await asyncio.sleep(30)  # backoff, then retry whole retrieval\n        chunks = await get_document_chunks(ds, doc_id, max_retries=10)\n    else:\n        raise","preventionTips":["Only fetch chunks after the document's parse status is fully complete","Budget generous max_retries/retry_delay for large documents","Add exponential backoff around whole chunk-retrieval calls","Monitor RAGFlow indexing lag to size retry budgets correctly"],"tags":["ragflow","race-condition","retry-exhausted"],"backgroundTag":"request-timeout","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}