Read-only fields in request body - ignore vs. 400

What is the recommended way to handle the presence of read-only fields in the request body?

For example, a user resource may have a permanently read-only attribute username. If a PATCH request comes in with the username specified, should the field be ignored (updating any other specified fields), or should the server return e.g. 400?

I’m currently returning an error response, because I like pushing clients to behave correctly instead of simply ignoring what may be bugs in the clients. (For this particular example, users may wonder why the username never seems to change correctly when submitting a form. Returning an error forces client devs to correctly implement the API specification, which of course states that username is read-only.)

FWIW, I’m working on an implementation and plan to error on read-only fields. But I’ll continue with the update to gather any other possible errors before returning the error objects (in case there are multiple problems with the update request).

I don’t think, that returns error, on try to write read-only attributes are right. Cause client never know which of attributes are read-only. So IMHO server should just ignore read-only fields. Like imagine that client at first have to identify attributes which are read-only and after that can send valid request, it’s ridiculous.

Cause client never know which of attributes are read-only.

They’d certainly know which field was read-only; permanently read-only fields would be documented, and the returned error object would also contain the prohibited field in pointer.

IMHO, not erroring would be a footgun for the API users. Because the client could say update “foobar” and you report back OK so the client assumes all is well in the world, but in reality the “foobar” change didn’t take effect and now you have a divergence in expected state (what the client shows the user and what is actually saved on the server).

I agree it may in many cases be a footgun, but PATCH (in general) returns the current resource state back to the client, and the returned resource should be used to update the client state. For example, other fields than the ones included in the patch request may be updated.