What's wrong
ObjectRouteHandler.BatchAsync (GitLfsCache/Endpoints/ObjectRouteHandler.cs ~lines 80–100) relays non-2xx responses verbatim. For any 2xx, it calls JsonNode.ParseAsync on the body with no try/catch. Only a null result is mapped to 502. Nothing above it catches JsonException: the route is served through MapFallback, and no exception handler is registered.
The body that does parse is then read in GitLfsCache/Batch/BatchRewriter.cs with direct GetValue<string>() / GetValue<long>() calls on oid, size, the action href, and header values. A value of the wrong JSON type makes these throw InvalidOperationException. For example, "size": "123" or a numeric header value.
The locks subsystem's As-built section says "all untrusted JSON is read through a JsonValues helper". The batch path was never moved over.
Failure scenarios
- The upstream, or a gateway in front of it, answers
POST …/info/lfs/objects/batch with 200 text/html (a forge sign-in or SSO interstitial, or a captive proxy). JsonNode.ParseAsync throws, and git-lfs gets a bare 500 from the cache.
- The upstream returns valid JSON with
"size": "123". BatchRewriter throws, and the result is again 500.
A null body already gets 502. These cases are the same kind of problem (the upstream sent something we can't use), but they surface as the proxy's own internal error. That points operators at the wrong component and hides upstream's actual response.
Suggested fix
- Catch
JsonException around the parse and answer 502 with a short reason, the same as the existing null-body branch. Relaying the upstream response verbatim when it isn't application/json is also reasonable.
- Read the batch fields in
BatchRewriter through the existing JsonValues helper, and treat a malformed object as a 502 or skip the entry, rather than throwing.
Acceptance criteria
- Tests with a stub upstream show that a 200
text/html batch body and a 200 JSON body with "size": "123" each produce 502 (or a verbatim relay), never 500.
What's wrong
ObjectRouteHandler.BatchAsync(GitLfsCache/Endpoints/ObjectRouteHandler.cs~lines 80–100) relays non-2xx responses verbatim. For any 2xx, it callsJsonNode.ParseAsyncon the body with no try/catch. Only anullresult is mapped to 502. Nothing above it catchesJsonException: the route is served throughMapFallback, and no exception handler is registered.The body that does parse is then read in
GitLfsCache/Batch/BatchRewriter.cswith directGetValue<string>()/GetValue<long>()calls onoid,size, the actionhref, and header values. A value of the wrong JSON type makes these throwInvalidOperationException. For example,"size": "123"or a numeric header value.The locks subsystem's As-built section says "all untrusted JSON is read through a JsonValues helper". The batch path was never moved over.
Failure scenarios
POST …/info/lfs/objects/batchwith200 text/html(a forge sign-in or SSO interstitial, or a captive proxy).JsonNode.ParseAsyncthrows, and git-lfs gets a bare 500 from the cache."size": "123".BatchRewriterthrows, and the result is again 500.A
nullbody already gets 502. These cases are the same kind of problem (the upstream sent something we can't use), but they surface as the proxy's own internal error. That points operators at the wrong component and hides upstream's actual response.Suggested fix
JsonExceptionaround the parse and answer 502 with a short reason, the same as the existing null-body branch. Relaying the upstream response verbatim when it isn'tapplication/jsonis also reasonable.BatchRewriterthrough the existingJsonValueshelper, and treat a malformed object as a 502 or skip the entry, rather than throwing.Acceptance criteria
text/htmlbatch body and a 200 JSON body with"size": "123"each produce 502 (or a verbatim relay), never 500.