skip to content

An OData client sends a ten-request multipart $batch and gets back four response parts, the last an error. What happened, and how does continue-on-error change that?

level: seniorimportance: should knowfreq 10%

answer

  1. in order, stop at first error
  2. missing parts never ran
  3. a preference on the batch, not parts
  4. the 4.0 name carries odata.

basics

~20 s

A multipart OData batch runs in order and, by default, stops at the first error, which becomes the last part; the six missing requests never ran. Prefer: odata.continue-on-error asks the service to report the error and keep going.

solid answer

~40 s

The multipart format requires the service to process the top-level requests and change sets in the order received, and processing stops at the first error unless the client asked otherwise; that error response is then the last part. So request four failed and requests five to ten were never executed. Sending `Prefer: continue-on-error` (OData 4.01) or `odata.continue-on-error` (its OData 4.0 name) on the **batch** request asks the service to return the error and continue. A service MUST ignore preferences it does not know, so a 4.0 service ignores the unprefixed name; 4.01 tells services to accept both and clients to send the `odata.` form for compatibility. The preference SHOULD NOT be applied to individual requests, and it never weakens a change set's all-or-nothing rule.

go deeper

for a junior

Recall that a multipart OData batch stops at the first error by default and that a preference on the batch can ask it to continue.

for a middle

Explain in-order processing, the error as the last part, the two names of the preference and where in the request it belongs.

for a senior

Diagnose missing parts as unexecuted work, re-send only what never ran, and check the preference's name and the service's advertised support.

for a principal

Decide per workflow whether partial progress is acceptable or whether the client should stop early, and make that choice explicit in the batch it builds.

## Reading the symptom A multipart batch response normally mirrors the request part for part. The OData protocol lists three exceptions, and one of them explains this symptom: when an error occurs while processing a request and the client did not ask to continue on error, **processing of the batch is terminated and the error response is the last part** of the multipart response. So ten requests in and four parts out, with the fourth an error, reads like this: - requests one to three were processed, and their parts show their results; - request four failed, and its error is the last part; - requests five to ten were **never processed**: they have no part and caused no change. The outer status is still `200 OK`, because the batch headers were valid. The client should re-send only the requests that never ran, after dealing with the failure; requests one to three were applied, so re-sending the whole batch would repeat them. ## The rule, by version and format | Format and version | Default after an error | With the preference | |---|---|---| | Multipart, OData 4.0 | the service MUST return the error and stop | `odata.continue-on-error`: return the error and keep processing | | Multipart, OData 4.01 | processing stops on the first error | `continue-on-error` with an explicit or implicit value of `true` keeps processing | | JSON, OData 4.01 | all requests are processed according to their dependencies | `continue-on-error=false` lets the service stop spending cycles once an error occurs in a dependency chain | The multipart default is "stop", and the preference turns continuation on. The JSON default is effectively "continue", and the client turns it off. With `continue-on-error=false` on a JSON batch, the response MAY omit response objects for requests that were not processed. ## Asking for it correctly 1. **Put it on the batch.** Send it in the `Prefer` header of the outer `POST`, for example `Prefer: odata.continue-on-error`. The protocol says the preference SHOULD NOT be applied to individual requests within a batch. 2. **Pick the name the service understands.** OData 4.0 called it `odata.continue-on-error`; 4.01 renamed it `continue-on-error`. Services that support it SHOULD also accept the old name for 4.0 clients, and clients SHOULD send `odata.continue-on-error` for compatibility with 4.0 services. 3. **Remember that unknown preferences are ignored.** A service MUST ignore preference values it does not support or does not know. An unprefixed `continue-on-error` sent to a 4.0 service is silently dropped, and the batch still stops at the first error: the exact symptom above. 4. **Check support.** A service MAY advertise support through the Capabilities vocabulary, with the `ContinueOnErrorSupported` property of the `BatchSupport` term, or the older `BatchContinueOnErrorSupported` term, now deprecated in its favour. A response MAY also list applied preferences in a `Preference-Applied` header. ## What the corrected request looks like ```http POST /sales/$batch HTTP/1.1 Host: api.example.com OData-Version: 4.0 Prefer: odata.continue-on-error Content-Type: multipart/mixed; boundary=batch_7 ``` With the preference honoured, the error no longer ends the response. The response again matches the request part for part, so the client gets ten parts, request four's error sits in fourth position, and requests five to ten carry their own results. ## What continue-on-error does not change - **Change-set atomicity.** A change set is still all or nothing, and a failed change set still comes back as one error part. - **Order.** Multipart top-level parts are still processed in the order received. - **The outer status.** It stays `200 OK`; the preference changes how many inner parts there are, not the envelope. - **Declared dependencies.** In a JSON batch, a request whose prerequisite failed is still not executed and still gets `424 Failed Dependency`. - **Who decides.** It is a preference: the client asks, and the service may decline. OData 4.01 also extends `continue-on-error` beyond batches, to delta updates and set-based updates and deletes, where it asks the service to keep applying changes after an error. ## A diagnosis checklist - Count parts against requests, and check whether the last part is an error. - Look for a `Prefer` header on the outer request, and for its name: prefixed or not. - Read the service's `OData-Version` and its Capabilities annotations for batch support. - Confirm the preference was not placed on the inner requests instead of the batch. - For a JSON batch, check whether the client sent `continue-on-error=false`, and which response objects are missing.

  • Why might adding Prefer: continue-on-error change nothing on some OData services?
    OData 4.0 named the preference `odata.continue-on-error`, and a service MUST ignore preferences it does not know, so a 4.0 service ignores the unprefixed 4.01 name and still stops at the first error. Clients SHOULD send `odata.continue-on-error` for compatibility. A service may also simply not support it; `ContinueOnErrorSupported` in its Capabilities `BatchSupport` annotation says whether it does.
  • Does an OData 4.01 JSON batch also stop at the first error by default?
    No. In a JSON batch, if `continue-on-error` is absent or true, all requests are processed according to their dependencies; a failure stops only its dependents, which get `424 Failed Dependency`. A client that wants the service to stop early sends `continue-on-error=false`, and the response MAY then omit objects for requests that were not processed.

saying these in an interview costs you the question

  • Requests after the failed one ran, but their responses were dropped.
  • continue-on-error makes a change set keep its successful operations.
  • continue-on-error belongs on each inner request that might fail.
  • A JSON batch also stops at the first error unless told otherwise.
  • Every OData service understands the unprefixed continue-on-error name.