fix(api): make single-entity delete resilient to file-cleanup failures (#1033)#1038
fix(api): make single-entity delete resilient to file-cleanup failures (#1033)#1038sanjibani wants to merge 1 commit into
Conversation
Closes basicmachines-co#1033. The DELETE /v2/projects/<id>/knowledge/entities/<uuid> endpoint returns 500 for non-markdown entities when the file cleanup step cannot resolve the file path (cloud storage target mismatch, missing local mirror, or already-deleted after a concurrent delete-directory pass). The DB delete is what callers actually need, so wrap file_service.delete_entity_file in try/except and log a warning instead of propagating the exception. Mirrors how delete_directory catches per-entity errors.
|
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6f2488b88
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| # of delete_directory, which catches per-entity errors instead of failing the batch. | ||
| try: | ||
| await self.file_service.delete_entity_file(entity) | ||
| except Exception: |
There was a problem hiding this comment.
Preserve failed file deletes in directory results
When delete_directory calls delete_entity, any real local filesystem failure from delete_entity_file (for example PermissionError on a read-only project or invalid file path state) is now swallowed here, so delete_directory increments successful_deletes and downstream MCP JSON reports deleted: true because failed_deletes == 0, even though the file was not removed. Since files are the source of truth and the next sync treats a remaining file with no DB row as new, narrow this suppression to the missing/remote cleanup cases or return a status that lets directory deletes report the per-file failure.
Useful? React with 👍 / 👎.
|
recheck |
|
Thanks for the contribution. Two contributor gates are still blocking this PR:
Once both checks are green, maintainers can take over any remaining implementation changes needed to get this merged. |
|
Thank you for investigating #1033 and proposing a defensive cleanup path. We verified the current single-entity delete implementation against the real non-Markdown file path and have now merged #1056, which asserts that the file, database row, and search entry are all removed. The broader catch-and-continue behavior in this PR could delete database state after an unexpected file-cleanup failure, leaving the source file available to be re-indexed later, so we do not want to take that implementation. Since the reported path is now covered by a regression test, I am closing this PR as superseded. We appreciate the contribution. |
Closes #1033.
The `DELETE /v2/projects//knowledge/entities/` endpoint returns 500 for non-markdown entities when the file cleanup step cannot resolve the file path (cloud storage target mismatch, missing local mirror, or already-deleted after a concurrent delete-directory pass). The DB delete is what callers actually need, so wrap `file_service.delete_entity_file` in try/except and log a warning instead of propagating. Mirrors how `delete_directory` catches per-entity errors.
In the reported bulk removal of 910 entities, all 339 non-
.mdentities failed with 500 even though `POST .../knowledge/delete-directory` (which shares the same `delete_entity` code path) cleared them with zero failures. That asymmetry is the surface symptom; the underlying fix is to make the single-entity path just as forgiving as the directory path.Regression test `test_delete_entity_succeeds_when_file_cleanup_raises` asserts `delete_entity` still returns `True` and the DB row is gone when the file service raises. Verified locally: passes with the fix, fails without it.