From 381091df71c23a46754be806ee53507a082fbcf9 Mon Sep 17 00:00:00 2001 From: bohdansolovie <153934212+bohdansolovie@users.noreply.github.com> Date: Thu, 11 Jun 2026 15:52:59 +0800 Subject: [PATCH] fix(dialog): guard async_ask() against empty or invalid kb_ids (#15530) Fixes #15529 . ### Problem `async_ask()` accessed `kbs[0]` without verifying that `KnowledgebaseService.get_by_ids()` returned any knowledge bases. Empty or stale `kb_ids` raised `IndexError`, which surfaced as HTTP 500 on search/bot SSE endpoints. ### Fix - Add an early guard when `kbs` is empty, yielding a final SSE error event (consistent with `gen_mindmap()` in the same module). - Add regression tests for empty `kb_ids` and deleted/invalid KB IDs. ### Test plan - [ ] `pytest test/unit_test/api/db/services/test_dialog_service_final_answer.py -k "async_ask_empty or async_ask_stale"` - [ ] Manual: `POST /api/v1/searchbots/ask` with invalid `kb_ids` returns SSE error, not HTTP 500 --------- Co-authored-by: Wang Qi --- api/db/services/dialog_service.py | 8 ++++ .../test_dialog_service_final_answer.py | 47 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/api/db/services/dialog_service.py b/api/db/services/dialog_service.py index b4af21b587..72e8c1cf1d 100644 --- a/api/db/services/dialog_service.py +++ b/api/db/services/dialog_service.py @@ -1600,6 +1600,14 @@ async def async_ask(question, kb_ids, tenant_id, chat_llm_name=None, search_conf include_reference_metadata, metadata_fields = _resolve_reference_metadata(search_config) kbs = KnowledgebaseService.get_by_ids(kb_ids) + if not kbs: + if not kb_ids: + error = "**ERROR**: No KB selected" + else: + error = "**ERROR**: The selected KB is not valid" + yield {"answer": error, "reference": {}, "final": True} + return + embedding_list = list(set([kb.embd_id for kb in kbs])) is_knowledge_graph = all([kb.parser_id == ParserType.KG for kb in kbs]) diff --git a/test/unit_test/api/db/services/test_dialog_service_final_answer.py b/test/unit_test/api/db/services/test_dialog_service_final_answer.py index 44b2bf570a..7fefb323a9 100644 --- a/test/unit_test/api/db/services/test_dialog_service_final_answer.py +++ b/test/unit_test/api/db/services/test_dialog_service_final_answer.py @@ -292,6 +292,53 @@ def test_async_ask_delta_events_carry_incremental_text_only(monkeypatch): ) +@pytest.mark.p2 +def test_async_ask_empty_kb_ids_yields_error_final_event(monkeypatch): + """ + When kb_ids is empty, async_ask() must not crash with IndexError on kbs[0]. + """ + monkeypatch.setattr( + dialog_service.KnowledgebaseService, "get_by_ids", lambda _ids: [] + ) + + events = _collect( + dialog_service.async_ask( + question="What is RAGFlow?", + kb_ids=[], + tenant_id="tenant-1", + ) + ) + + assert len(events) == 1 + final = events[0] + assert final.get("final") is True + assert "No KB selected" in final["answer"] + assert final["reference"] == {} + + +@pytest.mark.p2 +def test_async_ask_stale_kb_ids_yields_error_final_event(monkeypatch): + """Provided kb_ids that do not resolve to any KB should report invalid selection.""" + monkeypatch.setattr( + dialog_service.KnowledgebaseService, + "get_by_ids", + lambda ids: [] if ids == ["deleted-kb"] else [_KB], + ) + + events = _collect( + dialog_service.async_ask( + question="What is RAGFlow?", + kb_ids=["deleted-kb"], + tenant_id="tenant-1", + ) + ) + + assert len(events) == 1 + assert events[0].get("final") is True + assert "not valid" in events[0]["answer"] + assert events[0]["reference"] == {} + + # --------------------------------------------------------------------------- # Tests for async_chat (production code path) # ---------------------------------------------------------------------------