|
| 1 | +# MSCxxxx: `M_INCOMPATIBLE_SERVER` error code |
| 2 | + |
| 3 | +Following [MSC4291](https://github.com/matrix-org/matrix-spec-proposals/pull/4291) and |
| 4 | +[MSC4311](https://github.com/matrix-org/matrix-spec-proposals/pull/4311), the Server-Server API |
| 5 | +tells sending servers to convert certain federation errors into 5xx errors over the Client-Server |
| 6 | +API. [`PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}`](https://spec.matrix.org/v1.19/server-server-api/#put_matrixfederationv2send_joinroomideventid) |
| 7 | +and [`PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`](https://spec.matrix.org/v1.19/server-server-api/#put_matrixfederationv2inviteroomideventid) |
| 8 | +both say of their `400` response: |
| 9 | + |
| 10 | +> If `M_MISSING_PARAM` or `M_INVALID_PARAM` is returned and the request is associated |
| 11 | +> with a Client-Server API request, the Client-Server API request SHOULD fail |
| 12 | +> with a 5xx error rather than being passed through. |
| 13 | +
|
| 14 | +These rejections occur when the events in the request fail the validation introduced by those MSCs, |
| 15 | +for example when an invite lacks a full-PDU `m.room.create` event in its stripped state. The |
| 16 | +rationale for a 5xx is that there is nothing the client can do differently to make the request |
| 17 | +succeed. |
| 18 | + |
| 19 | +This translation has three problems: |
| 20 | + |
| 21 | +1. No specific status code or `errcode` is given, so implementations will diverge. Clients therefore |
| 22 | + cannot give a more helpful error message. |
| 23 | + |
| 24 | +2. Many clients and client SDKs automatically retry requests which fail with a 5xx status code. |
| 25 | + However, the failures in the MSCs are not transient as the two servers will remain incompatible |
| 26 | + until one of them is updated, which will not happen within any retry window. |
| 27 | + |
| 28 | +3. There is no good 5xx error code to use. 500 response rates are often monitored and assumed to |
| 29 | + mean something has gone wrong and needs fixing. 502/503 response are often interpreted by reverse |
| 30 | + proxies and load balancers as the backend being down, and stop sending traffic to it. The other |
| 31 | + defined 5xx errors don't strictly match, and are rarely used. |
| 32 | + |
| 33 | +This proposal replaces the 5xx translation with a `400` response carrying a new standard error code, |
| 34 | +`M_INCOMPATIBLE_SERVER`, meaning the request failed because the local server and a remote server it |
| 35 | +needed to communicate with are incompatible with each other. |
| 36 | + |
| 37 | +## Proposal |
| 38 | + |
| 39 | +A new standard error code is added to the Client-Server API: |
| 40 | + |
| 41 | +* `M_INCOMPATIBLE_SERVER`: The request could not be completed because it required communicating with |
| 42 | + a remote server which is incompatible with the local server. This is returned with a `400` HTTP |
| 43 | + status code. |
| 44 | + |
| 45 | +Clients SHOULD NOT automatically retry a request which failed with `M_INCOMPATIBLE_SERVER` and |
| 46 | +SHOULD surface the failure to the user. Servers MAY use the human-readable `error` field to describe |
| 47 | +the incompatibility. |
| 48 | + |
| 49 | +Proposals SHOULD strive to maintain backwards compatibility where feasible, rather than use |
| 50 | +`M_INCOMPATIBLE_SERVER`. |
| 51 | + |
| 52 | +The paragraph quoted above is replaced, on both the `/send_join` and `/invite` endpoints, with the |
| 53 | +following: |
| 54 | + |
| 55 | +> If `M_MISSING_PARAM` or `M_INVALID_PARAM` is returned and the request is associated with a |
| 56 | +> Client-Server API request, the Client-Server API request SHOULD fail with a |
| 57 | +> `400 M_INCOMPATIBLE_SERVER` standard Matrix error rather than the federation error being passed |
| 58 | +> through. There is nothing the client can materially do differently to make the request succeed, |
| 59 | +> so the error is shown to the user rather than retried. |
| 60 | +
|
| 61 | +For example, a client calling [`POST /_matrix/client/v3/rooms/{roomId}/invite`](https://spec.matrix.org/v1.19/client-server-api/#post_matrixclientv3roomsroomidinvite) |
| 62 | +where the invited user's server rejects the federation invite would receive: |
| 63 | + |
| 64 | +```json |
| 65 | +{ |
| 66 | + "errcode": "M_INCOMPATIBLE_SERVER", |
| 67 | + "error": "example.org rejected the invite because the two servers are incompatible. One of them may need updating." |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | +Note that the message does not say which server is at fault. On receiving the federation error, the |
| 72 | +sending server cannot know which of the two implementations is outdated. |
| 73 | + |
| 74 | +While the MSC4291 and MSC4311 validation failures are the motivating cases, the error code is |
| 75 | +defined generically. Other endpoints and future proposals MAY use `M_INCOMPATIBLE_SERVER` wherever |
| 76 | +a request fails because a remote server could not interoperate with the local server, rather than |
| 77 | +because of a client error or a fault within the local server itself. |
| 78 | + |
| 79 | +## Potential issues |
| 80 | + |
| 81 | +* Strictly speaking, `400` is the wrong class for this failure. The client's request was well-formed |
| 82 | + and the fault lies between the two servers. In practice, however, Matrix (and the lot of the web) |
| 83 | + treats 4xx responses as permanent failures and 5xx responses as potentially transient ones. The |
| 84 | + strict interpretation of the status ranges offers no third option for a final error that is |
| 85 | + no-one's fault (which is the case here). Matrix already returns `400` for failures outside the |
| 86 | + client's control, such as |
| 87 | + [`M_INCOMPATIBLE_ROOM_VERSION`](https://spec.matrix.org/v1.19/client-server-api/#other-error-codes). |
| 88 | + |
| 89 | +* The receiving server may return `M_MISSING_PARAM` or `M_INVALID_PARAM` because of a bug rather |
| 90 | + than version skew. The sending server cannot tell the difference, and does not need to. Either |
| 91 | + way the two implementations failed to interoperate, and retrying will not help. |
| 92 | + |
| 93 | +* This proposal is backwards compatible as the current text is only a SHOULD and does not specify |
| 94 | + the response body. Given the underspecified nature of the existing text, no client can be relying |
| 95 | + on the current behaviour anyway. |
| 96 | + |
| 97 | +## Alternatives |
| 98 | + |
| 99 | +We could keep the 5xx translation but specify it fully, e.g. as `502 M_INCOMPATIBLE_SERVER`. In |
| 100 | +strict HTTP terms `502 Bad Gateway` is the closest fit, since the local server is acting as a |
| 101 | +gateway to a remote server which returned an unusable response. However, using a 502 here goes |
| 102 | +against the common assumptions made by clients and proxies about such errors. |
| 103 | + |
| 104 | +## Security considerations |
| 105 | + |
| 106 | +None foreseen. |
| 107 | + |
| 108 | +## Unstable prefix |
| 109 | + |
| 110 | +None needed. |
| 111 | + |
| 112 | +## Dependencies |
| 113 | + |
| 114 | +None. |
0 commit comments