73 comments

[ 2.8 ms ] story [ 142 ms ] thread
I’ve always used 418 for this kind of thing
Ah, yes, the "I'm a teapot" status. Awesome! :-)
4xx seems more appropriate for sure. I'd say 400, 420, or 429 feel closest.
Ditto, for me 418 has always meant "check response body for the application error code".
I use 418 for “code should not have gotten here” type situations.

It's a weird enough and unique enough error that I pretty much know what is possibly up the moment I see it.

I think somebody also used it as a response when they detected the request to be a CSRF attack?
I always use 418 for anti-botting responses. First because it's hilarious and second because I want them to know that I know what they did heh.
I’m going to bet that every 999 could be defined more accurately using an existing code.

For me, if the server cannot specifically tell you what kind of error happened, it’s by definition a 500.

My API is internal, but it’s been nice to consider, “all 500-level errors are my team’s problem to fix or represent more accurately as a 400-level.”

Yep.

4xx: "You asked for something in a way that I can't respond" (auth error, unknown path, etc.)

5xx: "You asked for something in a valid way, but I can't respond right now" (outage, bug, etc.)

I think the semantics of 999 is "bugger off, you are not supposed to be here. I will not grace you with a proper status code". Because sending a status code, even a 4xx or a 5xx, acknowledges the request and plays along by giving a well-formed answer.

If you want to respond impolitely, you could just terminate the connection, or send plaintext "Don't scrape me, we are watching you" instead of a HTTP response, but I can imagine that doesn't work properly with proxies and middleware.

The conventional nonsense response "418 I am a teapot" has been standardized unfortunately. But `return Response("Request Denied", status=999)` is easy to write and does the job.

> you are not supposed to be here

Isn’t that a 403?

I’m not sure what value there is in saying “beep boop” rather than “you can’t come in.” If you really intend what you’re saying, why not just drop the tcp connection without any http response as you suggest?

I've never realized that http status 999 was a thing. I've often used 401 to tell user something similar to "denied"...which i get is different in that "unauthorized" (for 401) is the browser telling the user, while i suppose 999 would be the server telling the client to tell the user a generic "denied"...so there's a difference there, but a little nuanced i suppose. I guess TIL.
> I've never realized that http status 999 was a thing.

It’s not. That code does not conform to any standard.

Which isn't remotely the same thing as whether it's a 'thing'.
Technically correct. But one could argue that it shouldn't be a thing.
Then argue that. I can't even begin to catalog the things that shouldn't be, that are, even tho they aren't 'the standard'.
I don’t want to since I don’t think it should be a thing. If you want a catch-all error code, use 500 and accept the shame that comes with it.
It's not a thing and this post should be taken down before some bright kid figures it's a good idea to copy.
> When sending an HTTP GET or HEAD request to LinkedIn for a specific profile [...]

> It will also be returned if there are too many HTTP requests in a single day. This is similar to the HTTP 429 Too Many Requests error message.

Similar? That is excatly what 429 was made for, or not? This is weird or just lazy.

I believe this is a meme making fun at LinkedIn for using the wrong status code…
I’m assuming the logic is that LinkedIn uses 999 in combatting crawler bots so they don’t want to give reasons why the request is denied. That would help the bot overcome the restriction. e.g. it might be because you’ve sent too many requests but it also might be that your user agent is blocklisted.

Obviously it’s bad practise to do this but I don’t think it’s a mystery why they’d want their denial to be opaque. I’m also having trouble getting that worked up about it. If its OK to return 418 as a joke it’s probably fine to return 999 to suspected crawlers.

The correct status code for that is 400, the generic "something you did is wrong" or 403, the generic "go away".
Sure but I imagine you’d want to track real 400 errors (e.g. your POST request contains the wrong parameters) in analytics to ensure you haven’t introduced a bug in your code. Categorising bot repellant responses along with that would likely be very noisy.

Again I’m not really defending the practise, I think it’s bad, I can just clearly see how they ended up where they ended up.

Isn't that what 40x errors are for? E.g. I think there's 408 Bad Request for your POST example.
I suspect they intentionally break the HTTP spec for a similar reason: it will break some standard crawlers/bots, stop automatic retries, and things like that. This is trivial to account for of course, but it'll stop some script kiddies.
They might also have desired it for "zero-cost" observability: everything already monitors HTTP status codes, so they can monitor bot traffic without custom instrumentation.
Right, but then the crawler devs will google this weird 999 code and handle it as a 429.

If I wanted to mess with clients I don't like, I'd just return a random valid code.

The canonical response code for "you did something wrong but I can't/don't want to say what exactly" is 400.

I think if you don't want to supply even that, a better way would be to just close the connection and don't send anything back at all.

The only practical reason for a 999 error code I see is if you want to confuse the client about whether or not the response indicates an error at all. Maybe they were hoping some crawlers treat everything that's not 4xx or 5xx as "success" and so they can poison their index?

That thinking would be relatively naive though, as I think most http clients treat everything that's not 2xx as an error.

So most likely reason is probably some programmer that went through the REST fanboy phase and thought they were special.

> I think if you don't want to supply even that, a better way would be to just close the connection and don't send anything back at all.

I think this is the correct answer if a bad-mannered crawler has been identified. To take it a step further, one could do a HTTP version of a SSH Tarpit[1] although that's likely taking things too far.

[1] https://nullprogram.com/blog/2019/03/22/

Fun fact: ChatGPT (web version) returns 418 when you are able to get pass cloudflare but still get caught on their end. Very rare though
Either a 429 in the latter case or a method not allowed (iirc 405) in the former. 429 I kinda get not having since it’s less than a decade old which seems to be the cutoff point for some larger companies, but 405’s been around for a long while.
I think the solution is to try again. Bad behavior of not following protocols should be rewarded with bad behavior of not following protocols.
Reminds me of a similar one that only Shopify uses https://http.dev/430

It says it’s deprecated but we still get them from their API from time to time.

This is just wrong and bad.

The uses described should very clearly be 403 Forbidden where it’s refusing to respond based on user-agent, and 429 Too Many Requests where it’s rate limiting.

The spec says <https://www.rfc-editor.org/rfc/rfc9110#section-15-6>:

> Values outside the range 100..599 are invalid. Implementations often use three-digit integer values outside of that range (i.e., 600..999) for internal communication of non-HTTP status (e.g., library errors). A client that receives a response with an invalid status code SHOULD process the response as if it had a 5xx (Server Error) status code.

So, the spec declares it invalid, and its suggested handling yields the wrong semantics (server rather than client error).

Given the notes they have on things like 430, I’m astonished at them not noting the problems with this code.

> The uses described should very clearly be 403 Forbidden where it’s refusing to respond based on user-agent, and 429 Too Many Requests where it’s rate limiting.

That's precisely why LinkedIn returns 999, they're telling you to bugger off. Nobody thinks this is a good idea.

Looks like it was designed be wrong and bad to break web crawlers.
Ok it doesn't comply with the spec so? What harm does it cause?
Looks like it's the catch-all "I don't want to look up the proper status code for this" code.
This is super unprofessional. 999 means nothing, therefore it cannot be acted upon, therefore it is totally useless. Best description which I can come up with is "I don't like you and I won't say why, because I don't like you".

The expected client behavior is sending in return an empty http post request with wtf header and value "I feel this server is passive aggressive towards me."

Time to draft a pass-ag RFC
No, see, if you draft a pass-ag RFC, then the response codes in there are in some sense standard ones. So if we're going to go pass-ag on you, we're going to use some other codes.

And there's really no limit. If I'm going to be nonstandard, I could use more than three digits in the error code. I could use hex. I could use full text. The possibilities are limitless.

> therefore it cannot be acted upon, therefore it is totally useless

From the examples, this seem to be the point. If you receive a 429, you know you can just backoff a bit and it'll work at a later point, but if you receive 999, you're not sure how to proceed, which seems to be what they (the service) wants.

> This is super unprofessional. 999 means nothing, therefore it cannot be acted upon

Given that this seems to be bot-protection, that might actually be the point. It's basically saying; "I don't want you here, don't try to resolve this". Or in other words "F*ck off"

So, error 400.
Error 400 is "this is your fault, try better". But you don't want them to try better, you just want them to go away.
So, 500 “its not you, its me”.
But that's not it either. It's your damn fault you're blocked, but there's nothing to fix and I don't want you to try again with better arguments.
500 "Its not you, its me" was a joking reference to the cliche breakup line used to deflect discussion of what the recipient of the breakup could have done (or could still do) to avoid it.

Other options for this case:

403 Forbidden is probably the best fit. "The 403 (Forbidden) status code indicates that the server understood the request but refuses to fulfill it."

But it may not sufficiently communicate the "go away forever" aspect. Alternatives might include:

410 (maybe its not actually, strictly gone, but I'm never going to give it to you, so stop asking) Has the benefit of also being a 4xx error so focuses on it being a client error.

301 Moved Permanently Location: file:///dev/null

400 means "There's an error in your request, fix the error and try again"

IMO, it should be a simple 403 Forbidden.

The page explaining all the codes should make it clear that using status code 999 is a bad idea.

One reason why it's bad is because it violates RFC 2616 (§6.1.1):

    The first digit of the Status-Code defines the class of response. 
    The last two digits do not have any categorization role. 
    There are 5 values for the first digit:

      - 1xx: Informational - Request received, continuing process

      - 2xx: Success - The action was successfully received,
        understood, and accepted

      - 3xx: Redirection - Further action must be taken in order to
        complete the request

      - 4xx: Client Error - The request contains bad syntax or cannot
        be fulfilled

      - 5xx: Server Error - The server failed to fulfill an apparently
        valid request
See, that would require knowing why they are rejecting you. Is it us? Is it you? Hell we don't know and couldn't be arsed to figure it out, so, just...Nein. Nein Nein Nein.
> One reason why it's bad is because it violates RFC 2616

RFC 2616 is obsolete, as are the RFCs which obsoleted it; the reference here should be to §15 of RFC 9110.

It should be noted that nearly the same verbiage presented by the GP is used in RFC 9110. RFC 9110 also goes further to explicitly say that 999 is an invalid error code, but does indicate they should be treated as a 5xx.

> Values outside the range 100..599 are invalid. Implementations often use three-digit integer values outside of that range (i.e., 600..999) for internal communication of non-HTTP status (e.g., library errors). A client that receives a response with an invalid status code SHOULD process the response as if it had a 5xx (Server Error) status code.

> It should be noted that nearly the same verbiage presented by the GP is used in RFC 9110.

Yes, in case it wasn't clear, my intent was to correct the reference only, not the substance communicated by the reference.

So what if it violates an RFC, what harm does it do if you implement your own status codes?

Controversial and unpopular opinion, but RFC specs went out the window as soon as SPA apps came about.

If I can't use my back button without my page being hijacked to the home page of the site then to me RFCs are now just a defunct recommendation.

True, it's a bad idea because a spec says so? And when did I agree to comply to it?
I am wondering if it might have been chosen as a joke... 999 is a homonym for the German "nein, nein, nein" (no, no, no), which is rather fitting for a "go away" status code.
Or in the UK it's the telephone number for the emergency services, so suggesting "I'll call the police on you!" to unwelcome traffic.

More likely they just used it because it's one end of the 3 digit range and a "proper" code would leak information to the unwelcome traffic about what they did to get blocked (making it easier to avoid). Apparently some services already use 000 for other purposes (TIL).

And any new first digit for some theoretical protocol extension would use 6xx, so 999 is extra future-proof. Not that this is likely to ever matter, unless we introduce a status code category for "the interplanetary gateway is at fault" or something like that.
AFAIK this is a reference to a movie and the scene in question is Hitler shouting "nein" multiple times.

I highly doubt this is the origin. I think its just the last three digit number.

> 499 Please Spoof User Agent In A More Believeable Manner
Nginx has a similar unofficial code 444 - which just closes without any other information and it's super helpful. 999 OTOH feels like you're just too lazy to use the correct response though.

Edit: I should not that while you're using it in your Nginx config, you're not _actually_ returning it to the client. That's a fairly important distinction, and I think makes it more appropriate than the 999 thing.

Has anyone pointed out that in the UK 999 is the emergency number (like 911 in the US). So "999 Request Denied" sounds like a public safety issue to someone who doesn't speak tech. Make it 998 if you must.
I think if someone is looking at HTTP codes and extrapolating THAT from the response then there are other issues. At that point we might as well worry about people in Atlanta, GA not being able to call their mechanic if there's a 404 page.
Might as well returned Status: /dev/null