AnthropicClaude Certified Architect – Foundations2026-104.5 Design efficient batch processing strategies
Recover failed entries by their identifiers
Completed by the knowledge check
Learning objective
Reconcile unordered results using custom_id; inspect each errored result to fix invalid requests or retry transient failures, and resubmit expired items separately.
Core idea
A batch is a collection of independently processed requests. Its result file can list them in a different order from submission. Match every result through custom_id, then choose recovery from that entry’s result.type and error details.
An invalid request needs repair; a transient server failure can justify a retry; an expired entry needs another submission opportunity. These are different decisions. Replaying the whole batch loses that distinction and repeats work that already succeeded.
This lesson owns correlation and targeted recovery. See 4.5.3 for whether recovery fits the delivery target, and 4.5.5 for reducing preventable failures before submission.
How it works
Establish the join before submission
Each object in the batch’s requests list contains a custom_id beside its params. The identifier must be unique within that batch. The create reference permits 1–64 characters matching ^[a-zA-Z0-9_-]{1,64}$: letters, digits, underscores, and hyphens.
Choose identifiers that your application can resolve to the source item. For example, module-payments can identify a module analysis, while module-payments-p1 can identify a separately submitted part. Keep the mapping from the batch id and entry custom_id to the source input and the request configuration. This is an application record; uniqueness within a batch does not create a guide-wide or business-wide identifier contract.
The returned result repeats custom_id. Use it as the join key, never the result’s line number. A fast request appearing first in the file says nothing about its position in the submitted list. A consumer that zips requests and results by array index can attach a valid finding to the wrong module without encountering a parsing error.
When you split or modify failed work, preserve enough mapping to connect the replacement entries to the original source. Two chunk requests need distinct identifiers within their replacement batch. Giving both the original identifier would violate uniqueness and would also hide which portion each result describes.
Inspect the result envelope
After processing_status becomes ended, retrieve results through GET /v1/messages/batches/{message_batch_id}/results. The output is JSON Lines: each line is a JSON object describing one entry. The batch’s aggregate request_counts helps summarize the run, but the individual entries identify which sources need attention.
The following outcomes require different handling:
result.type | Meaning | Recovery decision |
|---|---|---|
succeeded | A message was created | Validate and consume result.message |
errored | The request failed without creating a message | Inspect result.error and classify the cause |
canceled | Cancellation prevented the entry from reaching the model | Resubmit only if the work is still required |
expired | The batch expired before the entry reached the model | Submit that missing work separately if still needed |
A terminal batch can contain a mixture of all these outcomes. ended is therefore the signal to read results, not permission to mark every source complete.
Execution success and answer acceptance also differ. A succeeded message might still require application review or continuation. Keep such work distinct from an API failure: the service did create a message. Repeated generation of that entry is not the same operation as retrying an unprocessed expired entry.
Read the nested error, then repair or retry
For raw JSON results, result.error is an error response envelope. The error classifier is result.error.error.type, and the corresponding explanation is result.error.error.message. The API results reference defines this nesting. Do not assume the classifier sits directly at result.error.type; that field identifies the outer error response.
An invalid_request_error calls for reading the explanation and changing the invalid input or configuration. When input alone exceeds the model’s context window, the API returns HTTP 400 with invalid_request_error and a message containing “prompt is too long”. In a batch result, inspect result.error.error.message for that explanation, then use the preflight and chunking approach in 4.5.5 before resubmission. Repeating exactly the same oversized request creates another opportunity for the same validation failure, not a repair.
An api_error (whose default message is “Internal server error”) or overloaded_error can enter a bounded retry path if the work is still useful and its delivery budget allows another attempt. Inspect the actual type and message rather than treating every errored result as transient. An unchanged retry does not repair invalid_request_error, authentication_error, permission_error, or billing_error; correct the request, credentials, permissions, or billing cause first.
The batch guide notes that individual request params are validated asynchronously. Submission acceptance therefore does not prove that every entry’s message request is valid. The eventual error belongs to the affected custom_id; it does not imply that successful sibling entries must be regenerated.
Resubmit missing work separately
An expired entry says that the processing opportunity ended before it reached the model. It is not evidence that its document was too large or that its prompt needs rewriting. Put the required expired entries into a replacement submission, with any other changes justified by independent evidence.
Choose the replacement path against the remaining delivery target. Another batch is suitable for work that can tolerate another deferred stage. A synchronous request may be the application policy for urgent missing output; see 4.5.3. In either case, exclude already accepted successes from transport-failure recovery.
Download results while they remain available: the batch guide specifies 29 days from created_at, not from ended_at. Losing access to the file would prevent the targeted reconciliation that the identifiers were meant to support.
Key distinctions and trade-offs
custom_id provides correlation, not an error policy. Two entries can be equally easy to identify and need completely different recovery actions. The result type narrows the decision; the nested error explains what must change.
Targeted recovery preserves valid results and limits repeated inference. It requires retaining the source-to-request mapping and processing every returned line. Aggregate counts alone are insufficient: knowing that five entries errored does not identify the five documents or explain their failures.
Do not equate selective resubmission with blindly retrying every non-success. Canceled work might no longer be wanted, invalid work needs correction, and expired work needs a separate processing opportunity. Each outcome has a reason.
Architecture / decision scenario
A weekly audit submits 400 module requests and receives 392 succeeded, five errored, and three expired results. The result file begins with module-auth, although that request was near the end of the submission list.
Join each line by custom_id and retain the 392 successful messages for validation. For the five errors, inspect result.error.error.type and .message. Suppose four report invalid_request_error with “prompt is too long” and one reports api_error. Split and preflight the four oversized sources, assign separate chunk identifiers, and retry the api_error entry if its budget permits.
Place the three expired entries into a replacement submission without inventing a document defect. Select batch or synchronous recovery from the publication target. Reconcile replacement outputs with the same source mapping, while leaving the 392 accepted results out of the failure rerun.
The final coverage report counts accepted analyses by source, including reconstructed chunked analyses. It does not infer coverage from the replacement batch’s existence or from the order in which results arrived.
Common exam traps
- Pairing results with requests by position instead of
custom_id. - Reading
endedas all-successful completion. - Treating all
erroredentries as transient server errors. - Looking for the raw JSON classifier at
result.error.typeinstead ofresult.error.error.type. - Resending invalid inputs unchanged or rewriting expired inputs without evidence.
- Rerunning every successful entry because a minority failed.
What to remember
Join by custom_id, branch on result.type, and inspect the nested error before choosing repair or retry. Expiration calls for a separate processing opportunity, while validation failure calls for a justified change. Preserve accepted successes and recover only the work that remains required.
Knowledge check
A weekly audit consumer pairs the batch’s requests with the lines of its results file by position. Several findings end up attached to the wrong modules, and no parsing error occurs. Which change fixes the pairing?
custom_idended before reading, so that the file is in submit ordercustom_id is repeated on each result precisely so it can serve as the join key. Neither submission order nor waiting for ended fixes the file’s order, and a module name inside the generated answer is model output rather than a reliable identifier.A batch consumer reads result.error.type from each raw errored line, gets the same value every time, and therefore retries every errored entry as transient. Which field should it branch on instead?
result.typeresult.message.stop_reasonresult.error.error.typeresult.error.error.messageresult.error is an error response envelope whose own type is always error ; the classifier, such as invalid_request_error or api_error , sits at result.error.error.type , with the explanation in result.error.error.message . result.type only says the entry errored , and an errored entry has no message to carry a stop reason.A batch of 600 module analyses ends with 588 succeeded, 7 errored and 5 expired entries. Five of the errors are invalid_request_error with “prompt is too long”, and two are overloaded_error. Which recovery plan fits?
max_tokensoverloaded_error can enter a bounded retry, and expired means the entry never reached the model — it needs another processing opportunity, not a rewrite. The 588 successes stay out of the rerun, and max_tokens limits output, not input.The replacement batch splits the oversized source module-billing into two parts. Which custom_id values should the two part requests use?
module-billing for both, so that results map back to the original sourcemodule-billing-p1 and module-billing-p2 , mapped to the source in your recordsmodule-billing/1 and module-billing/2 , mapped to the source in your recordscustom_id must be unique within a batch and match ^[a-zA-Z0-9_-]{1,64}$ — letters, digits, underscores and hyphens, so a slash is invalid. Distinct part identifiers, plus an application mapping back to the original source, keep each result attributable; one shared identifier would break uniqueness and hide which part a result describes.References
Anthropic, Batch processing
Anthropic, Create a Message Batch
Anthropic, Retrieve Message Batch results
Anthropic, Context windows