Skip to content

fix(batches): results survive input-file deletion, 410 once output/error files are deleted - #1741

Merged
pjb157 merged 9 commits into
mainfrom
fix/batch-results-deleted-input-file
Sep 15, 2026
Merged

pjb157 merged 9 commits into
mainfrom
fix/batch-results-deleted-input-file

Conversation

@pjb157

@pjb157 pjb157 commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

GET /ai/v1/batches/{batch_id}/results mishandled deleted files in two directions.

Input file deleted → was a 500, now served. Deleting an input file intentionally keeps the batch but unlinks it (file_id = NULL) and lets the orphan-purge daemon remove its request templates. The results view required the file so it could walk templates in line order, and failed with Batch has no associated file_id, even though every request row is self-contained. Now:

  • File present: unchanged. Walk templates in line order and join each to its request.
  • File deleted: walk the batch's requests in creation order and left-join whatever template still exists (LATERAL LIMIT 1 probe, same as get_request_detail). input_body is null once the template is gone; custom_id, model, status, response_body and error are unaffected.

Search/status filters, keyset pagination and the live + archive union apply on both paths. BatchResultItem.input_body keeps its type; its doc comment now says it can be null.

Output or error file deleted → was served anyway, now 410. Those files are virtual and created with the batch, so a missing output_file_id / error_file_id can only mean the user deleted it. Deleting a results file is a statement that those results should be gone, and the endpoint used to hand back the same rows regardless. It now returns 410 Gone naming the deleted file(s), on both the paginated and streaming paths. The batch itself stays readable.

Test plan

  • New test_batch_results_served_after_input_file_deleted: file_id = NULL with dangling template_ids returns 200 on /results and /results?limit=10, requests in creation order with input_body: null, completed/failed fields intact, X-Last-Line correct, ?status=failed still filters.
  • New test_batch_results_gone_after_results_file_deleted: output file, error file, or both deleted returns 410 on both paths with a message naming the file; GET /batches/{id} still 200.
  • Handler tests that hand-insert batches now attach virtual output/error files via a shared helper, as create_batch does. All 67 api::handlers::batches tests pass.
  • test_batch_results_stream_serves_archived_batches and the template_generation_migration integration tests pass.
  • cargo clippy -p fusillade-arsenal -p dwctl --tests clean for the changed code.
  • .sqlx metadata updated for the changed batches lookup. Locally cargo sqlx prepare --check also flags an unrelated, untouched query file (nullability drift against my local DB); leaving that to CI's fresh database.

🤖 Generated with Claude Code

Copilot AI lite review requested due to automatic review settings September 10, 2026 08:18
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 10, 2026

Copy link
Copy Markdown

Deploying control-layer with  Cloudflare Pages  Cloudflare Pages

Latest commit: 087f252
Status: ✅  Deploy successful!
Preview URL: https://8b1e367b.control-layer.pages.dev
Branch Preview URL: https://fix-batch-results-deleted-in.control-layer.pages.dev

View logs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The newly added 410 response message formatting includes unintended indentation whitespace due to a \-continued string literal, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR updates the GET /ai/v1/batches/{batch_id}/results handler to return 410 Gone when a batch’s input file has been deleted (leaving file_id = NULL), avoiding an incorrect 500 for a permanent, caller-caused state and documenting the behavior in the OpenAPI annotation.

Changes:

  • Add an early batch.file_id.is_none() guard in the batch results handler that returns Error::Gone (HTTP 410) with guidance to use output_file_id / error_file_id.
  • Extend the OpenAPI endpoint documentation to include a 410 response.
  • Add a regression test ensuring both streaming and paginated results paths return 410 after the batch input file is deleted, while GET /batches/{id} still returns 200.
File summaries
File Description
dwctl/src/api/handlers/batches.rs Adds a 410 Gone handler short-circuit for missing batch file_id, updates OpenAPI responses, and adds a focused test covering the new behavior.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1451 to +1455
message: format!(
"Results for batch {} are no longer available because its input file was deleted. \
Download `output_file_id` or `error_file_id` instead.",
batch_id_str
),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — this was addressed by rewriting the message construction to a single-line format! string (no \\-continuation, so no embedded indentation spaces can leak into the 410 detail). We also updated the related comment in delete_file (files.rs) to accurately describe the soft-delete behavior: non-terminal batches are cancelled and unlinked, output_file_id/error_file_id are NULLed, and the file is retained for audit, so existing batch results survive file deletion.

Deleting an input file intentionally keeps the batch but unlinks it
(file_id = NULL) and lets the orphan-purge daemon remove its request
templates. The results view on GET /ai/v1/batches/{id}/results required the
file to walk templates in line order, so it failed with a 500 ("Batch has no
associated file_id") even though every request row is self-contained and
the output/error file streams keep working.

Make the results stream tolerate a missing file: with a file, walk templates
in line order as before; without one, walk the batch's requests in creation
order and left-join whatever template still exists. input_body is null once
the template is gone; custom_id, model, status, response and error are
unaffected. Same search/status filters and keyset pagination on both paths.
@pjb157
pjb157 force-pushed the fix/batch-results-deleted-input-file branch from 48622a7 to 84a7716 Compare September 10, 2026 08:47
@pjb157 pjb157 changed the title fix(batches): return 410 for results of a batch whose input file was deleted fix(batches): serve results for a batch whose input file was deleted Sep 10, 2026
The output and error files are virtual and created with the batch, so a
missing output_file_id or error_file_id can only mean the user deleted that
file. Deleting a results file is the user's statement that those results
should be gone; the results endpoint used to serve the same request rows
anyway. Answer 410 Gone naming the deleted file instead, on both the
paginated and streaming paths. A deleted input file is unchanged: results
still stream with input_body null.

Handler tests that hand-insert batches now attach the virtual files the
way create_batch does.
@pjb157 pjb157 changed the title fix(batches): serve results for a batch whose input file was deleted fix(batches): results survive input-file deletion, 410 once output/error files are deleted Sep 10, 2026
pjb157 and others added 7 commits September 14, 2026 12:46
…ng error

The batches PR made get_batch_results_stream walk a batch's requests
directly when file_id is NULL, so results remain streamable after the
input file row is deleted. Rewrite the stale test that pinned the old
'Batch has no associated file_id' error to assert the new behavior:
results stream before and after deleting the input file, while output/
error-file deletion still yields 410.
@pjb157
pjb157 enabled auto-merge (squash) September 15, 2026 12:51
@pjb157
pjb157 merged commit 144c1e4 into main Sep 15, 2026
24 checks passed
sejori pushed a commit that referenced this pull request Sep 15, 2026
🤖 I have created a release *beep* *boop*
---


##
[11.11.0](v11.10.2...v11.11.0)
(2026-09-15)


### Features

* **onwards:** fail over streamed requests whose first token stalls
([#1780](#1780))
([2418bf8](2418bf8))


### Bug Fixes

* **batches:** results survive input-file deletion, 410 once
output/error files are deleted
([#1741](#1741))
([144c1e4](144c1e4))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants