Skip to content

UN-1412 [FIX] Mirror API deployment execute errors into message.error - #2334

Merged
Deepak-Kesavan merged 2 commits into
mainfrom
UN-1412-standardize-api-deployment-errors
Oct 12, 2026
Merged

Deepak-Kesavan merged 2 commits into
mainfrom
UN-1412-standardize-api-deployment-errors

Conversation

@Deepak-Kesavan

@Deepak-Kesavan Deepak-Kesavan commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What

  • Every error raised on the API deployment execute endpoint (POST /deployment/api/<org>/<api>/) now also carries message: {"execution_status": "ERROR", "error": "<text>"}, the slot successful and failed executions already report in.
  • The existing type / errors keys and every status code stay exactly as they are. This is additive only.
  • The status endpoint (GET ...?execution_id=) is deliberately left untouched (see Why).
  • The committed OpenAPI spec documents the new key on execute as DeploymentErrorResponse.
// 401 before
{"type": "client_error", "errors": [{"code": "error", "detail": "Unauthorized", "attr": null}]}
// 401 after
{"type": "client_error", "errors": [{"code": "error", "detail": "Unauthorized", "attr": null}],
 "message": {"execution_status": "ERROR", "error": "Unauthorized"}}

Why

  • UN-1412: a client of the execute endpoint gets message.error on an execution failure but type/errors on a rejected request (bad key, validation, 404, 429...), so it can't handle errors generically. QA re-opened the ticket on v0.173.1 with the Unauthorized body.
  • The {"error": "... is not a valid UUID."} body from the ticket no longer happens: a malformed key already returns the same 401 body as an unknown key.
  • Not on GET: on the status read, message is the result. unstract-python-client copies it into extraction_result without checking (client.py, body.get("message", "")). An error block there would be reported to callers as an extraction result.

How

  • DeploymentExecution.handle_exception adds the block to the exception handler's body on POST only (api_v2/error_message.py).
  • message.error joins the details with ; and leaves out attr. That is exactly how the published client already renders a refused request, so the error text callers see doesn't change.
  • An existing message key is never overwritten.
  • DeploymentAutoSchema rewrites the injected handler examples on execute so they match the new schema. The status operation keeps ErrorResponse.

Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)

  • No for existing keys. type, errors, status codes, the 200 body and the 422/500 execution bodies are byte-for-byte unchanged. Tests compare the response with what the project-wide handler alone produces.
  • The status endpoint (GET) is unchanged, and a test asserts it.
  • One visible change in the Python client: on a refused execute, its execution_status field is now "ERROR" (it was ""). The error text is the same. The client's pending logic only runs for 2xx, so polling behaviour doesn't change.
  • Generated clients/SDKs: the spec adds a DeploymentErrorResponse schema for execute errors. The downstream client/CLI regeneration should pick it up.
  • No other endpoints are affected (list_deployments, whoami, highlight, MCP, pipeline API).

Database Migrations

  • None

Env Config

  • None

Relevant Docs

  • specs/docstudio-oss.json regenerated (generate_docstudio_spec --check passes)

Related Issues or PRs

Dependencies Versions

  • None

Notes on Testing

  • Unit tests (api_v2/tests/test_deployment_error_message.py), all through DeploymentExecution.as_view():
    • every raised error on POST keeps the handler body and gains message
    • every raised error on GET is identical to the handler body
    • the schema's required fields match what the view sends
  • test_deployment_auth.py (integration) now also asserts message on each real rejection: unknown key, non-UUID key, inactive key, wrong API, wrong org.
  • Live on the dev env (deepak-unstract-dev) with real requests:
Request Status Body
POST, unknown key (valid UUID) 401 type/errors unchanged + message.error = "Unauthorized"
POST, key not a UUID 401 same as above (no more {"error": "... not a valid UUID"})
POST, no Authorization 403 + message.error = "Missing api key"
POST, valid key, no files 400 + message.error = "You must provide at least one file or presigned URL."
POST, timeout=9999 400 + message.error = "Ensure this value is less than or equal to 300."
POST, unknown API name 404 + message.error = "API not found"
POST, unlisted path (cloud middleware) 400 + message.error = "Invalid deployment api path"
GET status: unknown key / bad / unknown execution_id, unlisted path 401 / 400 / 404 / 400 unchanged, no message
POST, valid file (control) 200 unchanged {"message": {execution_id, execution_status: "PENDING", status_api, ...}}
GET status of the control 422 unchanged {"status": "PENDING", "message": null}

Screenshots

  • N/A (API only)

Checklist

I have read and understood the Contribution Guidelines.

Every 4xx/5xx raised on the deployment execute and status endpoints now
also carries {"message": {"execution_status": "ERROR", "error": ...}},
the key successful and failed executions already use. The existing
type/errors keys and status codes are unchanged, so clients parsing them
keep working.
…nt error text

On GET, message is the result payload and the published Python client
copies it out as extraction_result, so an error block there would read
as a result. Limit the message block to the execute endpoint and its
spec, and join details without attr, as the client already renders a
refused request.
@sonarqubecloud

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

via Greptile

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable defects were found.

Summary

Adds message.error and message.execution_status to handled POST execute errors without replacing existing response fields.

  • Execute errors include the same message field as execution outcomes.

Acknowledged scope and existing debt:

  • Deepak-Kesavan explicitly leaves GET unchanged because clients treat message as the extraction result.
  • Deepak-Kesavan explicitly assigns cloud middleware errors to a companion PR.
  • The execute 500 example differing from its schema is already recorded as known debt in _KNOWN_EXAMPLE_DIVERGENCES.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Deployment request raises an error] --> B[Existing exception handler]
  B --> C{POST?}
  C -->|Yes| D[Add message.error without replacing existing fields]
  C -->|No| E[Return existing body unchanged]
  D --> F[Return existing HTTP status]
  E --> F
Loading

Reviews (1) · Last reviewed commit: "UN-1412 [FIX] Self-review: keep the stat..." · Reviewed by Greptile

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
✅ e2e-api-deployment e2e 3 0 0 0 16.8
✅ e2e-coowners e2e 1 0 0 0 1.4
✅ e2e-etl e2e 1 0 0 0 14.6
✅ e2e-login e2e 2 0 0 0 1.3
✅ e2e-prompt-studio e2e 1 0 0 0 9.7
✅ e2e-smoke e2e 2 0 0 0 1.2
✅ e2e-workflow e2e 1 0 0 0 12.5
✅ frontend unit 623 0 0 0 14.0
✅ integration-backend integration 607 0 0 26 56.2
✅ integration-connectors integration 1 0 0 7 9.1
✅ integration-workers integration 164 0 0 1 38.7
❌ ui e2e 0 1 0 0 0.0
✅ unit-backend unit 1551 0 0 1 42.7
✅ unit-connectors unit 82 0 0 0 9.9
✅ unit-core unit 281 0 0 0 2.4
✅ unit-platform-service unit 18 0 0 0 2.2
✅ unit-rig unit 120 0 0 0 3.6
✅ unit-runner unit 12 0 0 0 2.5
✅ unit-sdk1 unit 843 0 0 0 40.4
✅ unit-tool-sidecar unit 8 0 0 0 1.5
✅ unit-workers unit 1412 0 0 1 116.0
TOTAL 5733 1 0 36 396.5

Critical paths

⚠️ Critical paths not yet covered

  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend
  • workflow-author — covered by integration-backend
  • co-owner-manage — covered by integration-backend, e2e-coowners
  • workflow-create-execute — covered by e2e-workflow
  • api-deployment-provision — covered by integration-backend
  • api-deployment-auth — covered by integration-backend
  • api-deployment-run — covered by e2e-api-deployment
  • mcp-server-auth — covered by integration-backend
  • mcp-platform-auth — covered by integration-backend
  • platform-key-whoami — covered by integration-backend
  • prompt-studio-author — covered by integration-backend
  • prompt-studio-fetch-response — covered by e2e-prompt-studio
  • connector-register-test — covered by integration-backend
  • pipeline-etl-execute — covered by e2e-etl
  • usage-aggregate-read — covered by integration-backend
  • usage-token-tracking — covered by e2e-api-deployment
  • callback-result-delivery — covered by e2e-api-deployment

@Deepak-Kesavan Deepak-Kesavan self-assigned this Oct 12, 2026
@Deepak-Kesavan
Deepak-Kesavan merged commit 9b3efba into main Oct 12, 2026
14 checks passed
@Deepak-Kesavan
Deepak-Kesavan deleted the UN-1412-standardize-api-deployment-errors branch October 12, 2026 17:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant