Repository navigation
fix(api): answer 400 for malformed JSON on POST /api/plugins and PATCH /api/relay/tokens/[id] - #17010
Merged
diegosouzapw merged 2 commits intoOct 18, 2026
Conversation
…H /api/relay/tokens/[id]
diegosouzapw
merged commit Oct 18, 2026
0533646
into
cheaper-inference:release/v3.8.52
40 of 60 checks passed
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.
Summary
POST /api/pluginsandPATCH /api/relay/tokens/[id]did not answer a request whose body is not JSON: the handler rejected instead. Both now answer 400 with{ "error": "Invalid JSON body" }. A well-formed body behaves as before.await request.json()with no guard, so theSyntaxErrorfrom a malformed body escapes the handler. The fix wraps that call in the same guard fix(gamification): answer 400 for malformed JSON on the rotate, servers and federation score routes #16730 added to three gamification routes; fix(api): answer 400 for a non-integer limit, offset or page on the dashboard and management list routes #16661, fix(chat): answer 400 when the JSON body is not an object #16668 and fix(api): answer 400 for a JSON body that is not an object on the logs, settings and providers routes #16671 answer 400 for other malformed inputs on other routes.Related Issues
pluginsorrelay).Target Branch
release/v3.8.xbranch (the highest openrelease/v*, hererelease/v3.8.52), notmainProof Of Fix (required)
tests/unit/api-malformed-json-400-plugins-relay.test.ts(new, 5 cases: a malformed body and a well-formed but invalid body for each route, and a malformedPATCHwith no credentials, which must still answer 401).release/v3.8.52with only the two route files reverted):Scope
Validation
8374-plugins-status-optional,plugins-route-error-sanitization,plugins-api-routes,api-malformed-json-400andrelay-tokens-always-protected, run together with the new file: 59 of 59).prettier --checkon the new test andeslintonplugins/route.tsand the new test are clean;check:ai-attributionandcheck:changelog-integritypass.eslintonrelay/tokens/[id]/route.tsreports two unusedrequestparameters on theGETandDELETEsignatures; they are on the base and this change adds none.Reviewer Notes
POST /api/pluginsruns with no password configured, as in the siblingapi-malformed-json-400.test.ts.PATCH /api/relay/tokens/[id]always requires management auth since fix(authz): keep relay token management authenticated when login is disabled #16976, so its requests carry a manage-scoped API key, as inrelay-tokens-always-protected.test.ts, and the unauthenticated case checks that a malformed body still gets 401: the JSON guard runs after the auth check. I did not run the routes under the Next.js server, so I did not measure which HTTP status an unhandled rejection becomes there; the test shows only that the handler rejects.changelog.d/fixes/PENDING-plugins-relay-json-400.md, one line, left for you to rename.plugins/route.tsalready failsprettier --checkon the base (the longzodchain); I left that chain as it is.