Skip to content

Don't delete container bundles when loading fails; write bundle files atomically#1859

Open
radheradhe01 wants to merge 1 commit into
apple:mainfrom
radheradhe01:fix/no-bundle-delete-on-load-error
Open

Don't delete container bundles when loading fails; write bundle files atomically#1859
radheradhe01 wants to merge 1 commit into
apple:mainfrom
radheradhe01:fix/no-bundle-delete-on-load-error

Conversation

@radheradhe01

Copy link
Copy Markdown
Contributor

Problem

ContainersService.loadAtBoot wraps each container load in a do/catch, and the catch runs try? FileManager.default.removeItem(at: dir) — deleting the entire on-disk bundle on any error. Some of those errors are transient and unrelated to bundle integrity. For example, a runtime plugin that is not yet registered throws internalError, which then destroys the user's container directory.

A contributing root cause: Bundle.write persists JSON with a plain data.write(to:) (non-atomic). A crash or power loss mid-write can leave a truncated file that fails to decode on the next boot — which then triggers the deletion path above, turning a recoverable torn write into permanent data loss.

Fix

  • Bundle.write: write with .atomic, so an interrupted write cannot leave a corrupt file behind.
  • loadAtBoot: stop deleting the bundle on load errors. Log and leave the bundle on disk so a transient failure (e.g. a plugin that registers later) does not cause data loss.

Notes

No behavior change on the success path.

… atomically

### Problem
`ContainersService.loadAtBoot` wraps each container load in a `do/catch`, and the `catch` runs `try? FileManager.default.removeItem(at: dir)` — deleting the entire on-disk bundle on **any** error. Some of those errors are transient and unrelated to bundle integrity. For example, a runtime plugin that is not yet registered throws `internalError`, which then destroys the user's container directory.

A contributing root cause: `Bundle.write` persists JSON with a plain `data.write(to:)` (non-atomic). A crash or power loss mid-write can leave a truncated file that fails to decode on the next boot — which then triggers the deletion path above, turning a recoverable torn write into permanent data loss.

### Fix
- `Bundle.write`: write with `.atomic`, so an interrupted write cannot leave a corrupt file behind.
- `loadAtBoot`: stop deleting the bundle on load errors. Log and leave the bundle on disk so a transient failure (e.g. a plugin that registers later) does not cause data loss.

### Notes
No behavior change on the success path.
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.

1 participant