fix!: azure_ai_search - raise when Azure AI Search rejects documents in a batch by ericdelorefice · Pull Request #4134 · deepset-ai/haystack-core-integrations · GitHub
Skip to content

fix!: azure_ai_search - raise when Azure AI Search rejects documents in a batch - #4134

Open
ericdelorefice wants to merge 1 commit into
deepset-ai:mainfrom
ericdelorefice:fix/azure-ai-search-batch-failures
Open

ericdelorefice wants to merge 1 commit into
deepset-ai:mainfrom
ericdelorefice:fix/azure-ai-search-batch-failures

Conversation

@ericdelorefice

Copy link
Copy Markdown
Contributor

Related Issues

No issue. Found by reading write_documents, following #3997, #4006 and #4027 in this repo. #3618 (by @HussainNizamani, for #3579) fixed a close relative in the Valkey store: a batch where some documents failed was reported with the wrong count.

Proposed Changes

SearchClient.upload_documents and merge_documents do not raise when individual documents fail. Azure AI Search answers the batch with 207 Multi-Status, and the SDK returns one IndexingResult per document with succeeded=False and a status_code (its docstring lists 409 for a version conflict, 422 when the index is temporarily unavailable and 503 when the service is too busy). The SDK's own SearchIndexingBufferedSender reads succeeded and retries 409, 422 and 503, so the SDK treats these as real failures.

write_documents and update_by_filter discarded that list and returned len(...) of what they sent. So when Azure rejected some documents:

  • write_documents returned the full count, and DocumentWriter reported every document as written. The rejected ones were simply missing from search afterwards, with nothing raised or logged.
  • A batch where every document failed also returned the full count.
  • update_by_filter returned the full count when some merges were refused.

The Elasticsearch, OpenSearch and Weaviate stores in this repo already check per-item results on write and raise DocumentStoreError.

This PR adds a small helper that checks the returned results and raises AzureAISearchDocumentStoreError (a DocumentStoreError) naming each failed document with its status code and message, capped at 10 lines plus "... and N more". It is called after upload_documents in write_documents and after merge_documents in update_by_filter. For example:

AzureAISearchDocumentStoreError: 1 of 3 documents were not written to index 'test-index':
Document 'doc-b': 503 Service unavailable.

Also in update_by_filter: its error message said "Failed to delete documents by filter", which this new error now passes through, so it now says "Failed to update documents by filter".

Breaking change

write_documents and update_by_filter now raise where they used to return a count, when Azure reports any document as not indexed. Documents Azure did index stay indexed; only the call's result changes. Code that relied on these calls never raising for a partly failed batch will now see the error. Marked fix!: for the same reason as #3997, #4006 and #4027; happy to drop the ! if you see it as a plain fix.

How did you test it?

Unit tests in tests/test_document_store.py, no Azure service needed. They use the real SearchClient; only the network is replaced, by a requests adapter mounted on the SDK's own RequestsTransport that answers as the service does (207 with one result per document for a partly failed batch). The document failures are injected this way. I have not seen this against a live Azure AI Search service.

  • test_write_documents_raises_when_documents_are_rejected: one of three documents rejected (503), and all three rejected (422).
  • test_write_documents_error_lists_at_most_ten_rejected_documents: 50 rejected documents give 10 lines and "... and 40 more".
  • test_update_by_filter_raises_when_documents_are_rejected: one merge refused with 409.
  • Controls, passing before and after: a fully successful write returns 3 and sends the three documents, a request-level 503 still raises HttpResponseError, and a successful update_by_filter returns 2 and sends the expected merge actions.

The four rejection tests fail on main with DID NOT RAISE DocumentStoreError and pass with the fix (checked three times each way). Outside the PR, I also checked that the SDK returns the 207 results without raising, and that DocumentWriter in a Pipeline reported 3 written on main where Azure indexed 2.

104 passed, 103 deselected                       # hatch run test:unit (Python 3.11, haystack-ai 3.4.0, azure-search-documents 12.0.0)
104 passed, 96 deselected                        # same, lowest-direct (haystack-ai 2.26.1, azure-search-documents 12.0.0)
4 failed, 100 passed, 103 deselected             # main's document_store.py with the new tests
All checks passed! / 19 files already formatted  # hatch run fmt-check (ruff 0.17.1)
Success: no issues found in 8 source files       # hatch run test:types (mypy 2.4.0)

Not run: the 103 integration tests, which need a live Azure service.

Notes for the reviewer

  • No retry is added. Retrying 409, 422 and 503 per document, as the SDK's buffered sender does, is a separate decision; this PR only stops those failures being reported as success.
  • delete_documents, delete_all_documents and delete_by_filter also ignore the per-document results of client.delete_documents. I kept this PR to writes and updates and can do the delete methods as a follow-up if you want them to behave the same way.

Checklist

  • I have read the contributors guidelines and the code of conduct
  • I added unit tests and updated the docstrings
  • I've used a conventional commit type in the PR title

This PR was written with AI assistance (Claude Code); the tests above were run before opening it.

…in a batch

upload_documents and merge_documents do not raise when individual documents
fail: the service answers 207 Multi-Status and the SDK returns one
IndexingResult per document with succeeded=False. write_documents and
update_by_filter discarded that list and returned the full count, so
rejected documents were reported as written or updated.

Both now raise AzureAISearchDocumentStoreError naming the failed documents
(first 10, then a count). Documents that were indexed stay indexed. Also
corrects update_by_filter's error message, which said "delete".
@ericdelorefice
ericdelorefice requested a review from a team as a code owner October 20, 2026 14:42
@ericdelorefice
ericdelorefice requested review from bogdankostic and removed request for a team October 20, 2026 14:42
@github-actions

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot added the type:documentation Improvements or additions to documentation label Oct 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage report (azure_ai_search)

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  integrations/azure_ai_search/src/haystack_integrations/document_stores/azure_ai_search
  document_store.py 789
Project Total  

This report was generated by python-coverage-comment-action

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration:azure-ai-search type:documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant