[.NET] Always return JSON from workflow status and respond endpoints - #60
Open
Shyju Krishnankutty (kshyju) wants to merge 2 commits into
Open
[.NET] Always return JSON from workflow status and respond endpoints#60Shyju Krishnankutty (kshyju) wants to merge 2 commits into
Shyju Krishnankutty (kshyju) wants to merge 2 commits into
Conversation
The workflow status and respond endpoints always write JSON on success but route every failure through CreateErrorResponseAsync without passing acceptsJson, so they fall back to AcceptsJson(req). That helper requires an explicit Accept header naming application/json, which means a client that sends no Accept header gets JSON on 200/202 and text/plain on 400/404. These tests assert JSON on both the success and error paths for all eight error call sites, across Accept: absent, text/plain, application/json, and */*. They fail today for every value except application/json. RespondToWorkflowAsync_MalformedBody also fails for application/json. The catch (JsonException) around ReadFromJsonAsync never fires: the worker's HttpRequestDataExtensions.ReadFromJsonAsync ends in ContinueWith(t => TryCast(t.Result)), and t.Result on a faulted task throws AggregateException. Malformed bodies surface as an unhandled 500 instead of the intended 400. Relates to #56 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 062445ed-7aad-4643-87b4-d1bc7313364a
Both endpoints write JSON unconditionally on success but routed every failure through CreateErrorResponseAsync without an acceptsJson argument. That falls back to AcceptsJson(req), which requires an explicit Accept header naming application/json, so a client sending no Accept header received JSON on 200/202 and text/plain on 400/404. The response shape flipped within a single endpoint, precisely when the caller was trying to determine what went wrong. Neither endpoint has a plain-text representation, so both now force JSON at all eight error call sites rather than negotiating. This differs from the run endpoint, which keeps ShouldReturnWorkflowJson because it does have a meaningful text form. Also fixes malformed request bodies on the respond endpoint. The worker's HttpRequestDataExtensions.ReadFromJsonAsync ends in ContinueWith(t => TryCast(t.Result)), and Task.Result on a faulted task throws AggregateException, so catch (JsonException) never fired and a bad body surfaced as an unhandled 500 instead of the intended 400. Fixes #56 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 062445ed-7aad-4643-87b4-d1bc7313364a
Copilot started reviewing on behalf of
Shyju Krishnankutty (kshyju)
August 3, 2026 18:14
View session
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes the .NET Azure Functions built-in workflow status and respond HTTP endpoints so they consistently return JSON on both success and failure, independent of the request’s Accept header, and restores the intended 400 Bad Request behavior for malformed JSON request bodies.
Changes:
- Force JSON error responses for
status/{runId}andrespond/{runId}by passingacceptsJson: truethrough all error paths. - Broaden JSON-deserialization error handling to correctly map malformed bodies to
400 Bad Request(includingAggregateException-wrappedJsonExceptioncases). - Add unit tests covering success and all error scenarios across multiple
Acceptheader values, and document the breaking behavior in the hosting CHANGELOG.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/BuiltInFunctions.cs | Forces JSON on all error paths for status/respond endpoints and widens malformed-body exception handling to return 400. |
| dotnet/tests/Microsoft.Agents.AI.Hosting.AzureFunctions.UnitTests/BuiltInFunctionsWorkflowJsonEndpointTests.cs | Adds coverage to ensure status/respond endpoints return JSON for both success and error across Accept variants. |
| dotnet/src/Microsoft.Agents.AI.Hosting.AzureFunctions/CHANGELOG.md | Adds an Unreleased entry documenting the breaking behavior change and malformed-body fix. |
Shyju Krishnankutty (kshyju)
marked this pull request as ready for review
August 3, 2026 18:35
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #56 (the remaining half — #52 already fixed the
202run response).Problem
status/{runId}andrespond/{runId}always write JSON on success, but their error paths fell through toAcceptsJson(req), which needs an explicitAccept: application/json. Callers sending noAcceptheader — or*/*, curl's default — got JSON on success andtext/plainon failure from the same endpoint.Separately, the malformed-body guard never fired:
ReadFromJsonAsyncreadsTask.Resultin aContinueWith, so bad JSON arrives asAggregateExceptionwrappingJsonException. The request failed as an unhandled 500 instead of 400.Change
runendpoint still does, since it has one.400 Bad Request.Python parity
Python already behaves this way.
_build_error_responsein_app.pyhardcodesmimetype=application/jsonwith noAcceptinspection, and bothworkflow/{name}/status/{id}andworkflow/{name}/respond/{id}/{requestId}return JSON on success and error alike. This brings .NET in line.Tests
40 cases (10 scenarios x 4
Acceptvalues). First commit is intentionally red (15/25), second turns it green.