diff --git a/haiku_rag_slim/haiku/rag/ingester/workers/pipeline.py b/haiku_rag_slim/haiku/rag/ingester/workers/pipeline.py index 3cf97c47..d626adf9 100644 --- a/haiku_rag_slim/haiku/rag/ingester/workers/pipeline.py +++ b/haiku_rag_slim/haiku/rag/ingester/workers/pipeline.py @@ -57,6 +57,9 @@ def _classify(exc: BaseException) -> Exception: # ProxyError — every transport-layer failure that's worth retrying. return TransientError(f"network: {exc}") + if isinstance(exc, FileNotFoundError): + return PermanentError(f"file not found: {exc}") + if isinstance(exc, asyncio.TimeoutError | TimeoutError | OSError): return TransientError(f"timeout/io: {exc}") diff --git a/tests/ingester/test_pipeline.py b/tests/ingester/test_pipeline.py index 52d89121..64e523f8 100644 --- a/tests/ingester/test_pipeline.py +++ b/tests/ingester/test_pipeline.py @@ -261,3 +261,12 @@ async def test_existing_permanent_error_passes_through_unchanged(): with pytest.raises(PermanentError) as excinfo: await run_job(client, _job()) assert excinfo.value is sentinel + + +@pytest.mark.asyncio +async def test_file_not_found_classified_as_permanent(): + """A deleted file should go straight to the DLQ, not retry.""" + client = _mock_client() + client.create_document_from_source.side_effect = FileNotFoundError("gone") + with pytest.raises(PermanentError, match="file not found"): + await run_job(client, _job())