Skip to content

Commit 3cc28d8

Browse files
authored
Merge pull request #2117 from nightcityblade/fix/issue-2116
fix(docker): preserve single-url crawl failure details
2 parents ea26abb + 4fc16ca commit 3cc28d8

2 files changed

Lines changed: 72 additions & 11 deletions

File tree

‎deploy/docker/api.py‎

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,15 @@ def _attach_declarative_hooks(crawler, hooks_config: dict) -> dict:
9292

9393
logger = logging.getLogger(__name__)
9494

95+
96+
def _raise_for_crawl_failure(result):
97+
if not result.success:
98+
raise HTTPException(
99+
status_code=status.HTTP_502_BAD_GATEWAY,
100+
detail=result.error_message,
101+
)
102+
103+
95104
# --- Helper to get memory ---
96105
def _get_memory_mb():
97106
try:
@@ -147,11 +156,7 @@ async def handle_llm_qa(
147156
enforce_egress(browser_cfg)
148157
crawler = await get_crawler(browser_cfg)
149158
result = await crawler.arun(url)
150-
if not result.success:
151-
raise HTTPException(
152-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
153-
detail=result.error_message
154-
)
159+
_raise_for_crawl_failure(result)
155160
content = result.markdown.fit_markdown or result.markdown.raw_markdown
156161

157162
# Create prompt and get LLM response
@@ -179,6 +184,8 @@ async def handle_llm_qa(
179184
)
180185

181186
return response.choices[0].message.content
187+
except HTTPException:
188+
raise
182189
except LLMProviderNotAllowed as e:
183190
raise HTTPException(status_code=400, detail=str(e))
184191
except Exception as e:
@@ -389,11 +396,7 @@ async def handle_markdown_request(
389396
)
390397
)
391398

392-
if not result.success:
393-
raise HTTPException(
394-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
395-
detail=result.error_message
396-
)
399+
_raise_for_crawl_failure(result)
397400

398401
return (result.markdown.raw_markdown
399402
if filter_type == FilterType.RAW
@@ -1023,4 +1026,4 @@ async def _runner():
10231026
except HTTPException:
10241027
await redis.delete(f"task:{task_id}")
10251028
raise
1026-
return {"task_id": task_id}
1029+
return {"task_id": task_id}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from types import SimpleNamespace
2+
3+
import pytest
4+
from fastapi import HTTPException
5+
6+
from api import _raise_for_crawl_failure
7+
8+
9+
def test_crawl_failure_is_reported_as_bad_gateway():
10+
result = SimpleNamespace(success=False, error_message="Blocked by anti-bot protection: challenge")
11+
12+
with pytest.raises(HTTPException) as raised:
13+
_raise_for_crawl_failure(result)
14+
15+
assert raised.value.status_code == 502
16+
assert raised.value.detail == result.error_message
17+
18+
19+
@pytest.mark.parametrize(
20+
("method", "path", "payload"),
21+
[
22+
("post", "/md", {"url": "https://example.com", "f": "raw"}),
23+
("get", "/llm/example.com?q=summarize", None),
24+
],
25+
)
26+
def test_single_url_crawl_failure_reaches_client(
27+
stock_client, server_module, monkeypatch, method, path, payload
28+
):
29+
error_message = "Blocked by anti-bot protection: challenge"
30+
failed_result = SimpleNamespace(success=False, error_message=error_message)
31+
32+
class FailedCrawler:
33+
async def arun(self, *args, **kwargs):
34+
return failed_result
35+
36+
async def get_failed_crawler(*args, **kwargs):
37+
return FailedCrawler()
38+
39+
async def release_crawler(*args, **kwargs):
40+
return None
41+
42+
import api
43+
import crawler_pool
44+
from auth import create_access_token
45+
46+
monkeypatch.setattr(api, "validate_url_destination", lambda url: None)
47+
monkeypatch.setattr(crawler_pool, "get_crawler", get_failed_crawler)
48+
monkeypatch.setattr(crawler_pool, "release_crawler", release_crawler)
49+
50+
token = create_access_token({"sub": "test@example.com"})
51+
request = getattr(stock_client, method)
52+
kwargs = {"json": payload} if payload is not None else {}
53+
response = request(
54+
path, headers={"Authorization": f"Bearer {token}"}, **kwargs
55+
)
56+
57+
assert response.status_code == 502
58+
assert response.json() == {"detail": error_message}

0 commit comments

Comments
 (0)