Skip to content

fix(service-client): keep the presigned rejection when the InBody fallback fails - #5003

Open
dolfim-ibm wants to merge 2 commits into
mainfrom
claude/docling-mcp-saas-fallback-m0isi0
Open

dolfim-ibm wants to merge 2 commits into
mainfrom
claude/docling-mcp-saas-fallback-m0isi0

Conversation

@dolfim-ibm

Copy link
Copy Markdown
Member

When the server rejects the presigned_url target, the service client falls back to the inbody target. If the server also rejects inbody, the client raised only the inbody error and hid the actionable cause (for example, artifact storage is not enabled). This showed up with docling-mcp in remote mode against a deployment that allows only presigned_url/storage targets: users saw only target kind 'inbody' is not allowed.

The client now keeps both rejections in the error detail, in the sync submit/convert path and in both async fallback paths. Fallback errors other than 400/422 (for example usage-limit errors) are raised unchanged.

Checklist:

  • Documentation has been updated, if necessary.
  • Examples have been added, if necessary.
  • Tests have been added, if necessary.

Review evidence:

  • Before: Task submission failed. (status=422, detail=target kind 'inbody' is not allowed. Allowed values: ['presigned_url', 's3', 'zip'].)
  • After: ... target kind 'inbody' is not allowed. ... The client tried the InBody target because the server rejected the presigned_url target: Presigned URL target requires artifact storage to be configured and enabled on the server.
  • Reproduced with a stub server that rejects presigned (artifact storage) and inbody (policy).
  • New test: test_submit_auto_reports_presigned_rejection_when_inbody_is_also_rejected.
  • uv run pytest tests/test_service_client_sdk_unit.py tests/test_service_client_fake_service.py: 205 passed.
  • make validate hooks (prek) on the changed files: passed.

…lback fails

When a server rejects the presigned_url target and then also rejects the
InBody fallback, the client raised only the InBody error. This hid the
actionable cause, for example that artifact storage is not enabled.
The client now reports both rejections in the error detail.

Signed-off-by: Michele Dolfi <dol@zurich.ibm.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ DCO Check Passed

Thanks @dolfim-ibm, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

@github-actions github-actions Bot added ai:ci-safe AI triage found no CI-safety concern (advisory) bug Something isn't working error-handling labels Oct 25, 2026
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

AI triage (advisory)

Checked commit bf7d0f8834. The analysis read the diff only. It did not run code from this pull request.

This pull request preserves the initial presigned URL rejection details when the InBody target fallback also fails in the service client. It also adds a unit test for this behavior.

CI safety: no concern found. A maintainer must still approve CI.

The ai:* labels are advisory. They do not approve CI and they do not add tests:full. A new push runs this check again.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI first review (advisory): no blocker found

This review covers the full diff at fbf2c39204. It read the code and did not run it.

The change is small and the logic is correct for the main case. One defect remains in the combined message: when the InBody rejection has no string detail, the message starts with the text "None". The reviewer does not need to check anything else for merge.

A maintainer makes the merge decision. This review never approves the pull request.

Comment thread docling/service_client/client.py Outdated
fallback_exc.message,
status_code=fallback_exc.status_code,
detail=(
f"{fallback_exc.detail} The client tried the InBody target because "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Combined detail starts with "None" when the InBody error has no detail

ServiceError.detail is str | None. _http_error_detail returns None when the response detail is not a string. FastAPI validation errors use a list for detail. In that case the InBody 400/422 error has detail=None. The f-string then gives None The client tried the InBody target because .... Only use fallback_exc.detail when it is set. Otherwise start the text at "The client tried ...". The ServiceError.__str__ already omits an empty detail. This is a message defect only, so it does not block the merge.

]


def test_submit_auto_reports_presigned_rejection_when_inbody_is_also_rejected() -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Test covers only the sync path

The PR changes three call sites: the sync _submit_conversion_job_with_auto_target, AsyncDoclingServiceClient.submit, and process_one in submit_and_retrieve_each. The new test covers only the sync path. The two async copies of the same try/except block have no test. A single async test for submit that uses the same fake would catch a later divergence between the copies.

@github-actions github-actions Bot added the ai:review-lgtm AI first review found no blocker or major issue (advisory) label Oct 25, 2026
@codecov

codecov Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 42.85714% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
docling/service_client/_async_client.py 14.28% 12 Missing ⚠️
docling/service_client/client.py 71.42% 2 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

When the InBody rejection has no string detail (for example, a FastAPI
validation error with a list detail), the combined message started with
the text "None". Add the InBody detail only when it is set.

Signed-off-by: Christoph Auer <cau@zurich.ibm.com>
@github-actions github-actions Bot added ai:ci-safe AI triage found no CI-safety concern (advisory) and removed ai:ci-safe AI triage found no CI-safety concern (advisory) ai:review-lgtm AI first review found no blocker or major issue (advisory) labels Oct 26, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI first review (advisory): no blocker found

This review covers the changes since fbf2c39204 at bf7d0f8834. It read the code and did not run it.

The update removes the None prefix when the InBody error detail is empty. The reviewer must check that the async error handling matches the sync error handling.

Earlier findings

  • fixed (suggestion): Combined detail starts with "None" when the InBody error has no detail
  • open (suggestion): Test covers only the sync path

A maintainer makes the merge decision. This review never approves the pull request.

@github-actions github-actions Bot added the ai:review-lgtm AI first review found no blocker or major issue (advisory) label Oct 26, 2026

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

ai:ci-safe AI triage found no CI-safety concern (advisory) ai:review-lgtm AI first review found no blocker or major issue (advisory) bug Something isn't working error-handling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants