GetAsync and PostAsync No Longer Error with HTTP Errors

image

I received a report that Nexus Embedded Editor (a Studio plugin) started throwing errors because of a JSON parsing error. This was caused by HttpService::GetAsync not throwing an error for an HTTP 400 error, leading for the code to try to parse it. This bug means that any system using the HttpService that relies on handling errors based on an error existing no longer will handle errors. This happens in both Studio and in-game, and my guess is it started with the last update. It can be reproduced by running the following on the server with the HttpService enabled:

--Only 1 is needed to reproduce. This just shows it isn't only for specific errors.
--Normally, an error should be thrown. With the bug, the error message is just printed.
print(game.HttpService:GetAsync("https://httpstat.us/400"))
print(game.HttpService:GetAsync("https://httpstat.us/404"))
print(game.HttpService:GetAsync("https://httpstat.us/500"))
print(game.HttpService:GetAsync("https://httpstat.us/502"))
13 Likes

Hi @TheNexusAvenger,

Previously, we had some disparity in HttpService when running in studio vs in production. About a week ago we standardized this, and I just realized that there was a functional change in regards to what counts as an error. Previously, non 2xx HTTP response codes were treated as an error, however that is no longer the case. I think this is more correct, since getting a status code (even if its 4xx or 5xx) means you managed to communicate with the endpoint and got a response back. Now only transport errors (timed out, failed to connect, dns issue, etc ) are counted as errors. Sorry for any inconvenience this might have caused.

  • boxheadedperson
4 Likes

Is there an intended way to differentiate an error vs a normal response if it is an external service where we can’t code for all the possible error messages (they aren’t publicly listed, they change, there are too many, etc)? Additionally, there are existing games and plugins that rely on this behavior and haven’t changed to using HttpService::RequestAsync which will no longer properly handle errors.

2 Likes

You could try to generalize based on the status code group?
2xx - oK
3xx - redirection
4xx - client error
5xx - server error

I would hope external http services abide by this accepted convention.

Production servers have not been treating 4xx and 5xx status as errors for a while, so theres no change there. The change is that now in studio, you see the same behavior as production servers/games.

  • boxheadedperson
2 Likes

GetAsync and PostAsync only returns the content, not the entire response like RequestAsync does, meaning all we can do is try to parse the message to see if it was an error. This can be very difficult for websites that could return an HTML document as an error. The plugins that I need to fix never used this, and I doubtful that the game developers who thought GetAsync/PostAsync would fail on an HTTP error (like all other Roblox services do) employ this.

1 Like

Oh I see what you mean now :frowning: In the case of GetAsync and PostAsync you can try to parse/interpret the response body and hope it was a successful transfer. It’s not ideal. I would really advocate switching over to RequestAsync if possible. Sorry about disrupting your work with this change, I will make sure to communicate better in case there is a similar change in the future.

  • boxheadedperson
3 Likes

Legacy code that uses GetAsync and PostAsync do not expect this changed behavior – they expect an error when the request returns 4xx or 5xx. You could potentially be breaking or crippling legacy error logging / retry systems.

None of my existing code that uses GetAsync or PostAsync will try to check if the string is an error string or a json response before json parsing it. Nobody expects this.

Roblox shouldn’t just randomly change the behavior of commonly used API members like this without a heads-up beforehand. The documentation isn’t even updated to reflect this changed behavior…

Change should be reverted and re-evaluated / properly announced.

9 Likes

Something else I thought of yesterday is that this behavior is now inconsistent with how the internal services handle errors. When an external service has an HTTP error, such as the DataStoreService, MessagingService, or the deprecated PointsService, they throw an error that is meant to be caught with pcall. I am certain all developers who do this already did this with GetAsync and PostAsync because that would be consistent. This change to make it so it universally doesn’t do it makes it inconsistent for older systems or people writing newer services since they aren’t deprecated.

1 Like

As an example, I actually wrote this exact piece of code just yesterday to quickly prototype something:

local success, content = pcall(function()
	return httpService:GetAsync(url)
end)
if success and content then
	return content
end
warn("GetAsync failed: " .. tostring(content))
return nil

I do admit, this is mostly out of laziness and quick prototyping reasons, so I didn’t want to use RequestAsync. Still, this is common practice, and most Roblox developers expect web requests from any service to be able to throw errors and pcall them as such.

2 Likes

Hello devs,

I heard you loud and clear and I understand its not fair to just change the behavior of a service you use and rely on. We have restored the previous behavior where we throw errors on bad http status code. Let me know if you run into any issues or inconsistencies and I can try to help out. Sorry for any trouble!

-boxheadedperson

14 Likes