kopia/kopia · error
error writing format blob
Error message
error writing format blob
What it means
During `kopia repository repair`, after a replica of the repository format blob is found via RecoverFormatBlob, Kopia writes it back under the canonical blob ID (format.KopiaRepositoryBlobID) with PutBlob. If that write fails, the error is wrapped as "error writing format blob". This means a replica was found but the storage backend refused the write that would restore it.
Solutions
- Inspect the wrapped puterr to identify the backend-specific cause (permissions, quota, transient).
- Verify the storage location is writable (IAM write permissions, disk space, not mounted read-only).
- Re-run the repair; the listing step will re-find the replica and retry the write.
- Use `--dry-run` first to confirm a replica exists before attempting the real write.
Example fix
// before
if puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {
return errors.Wrap(puterr, "error writing format blob")
}
// after
if puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {
return errors.Wrap(puterr, "error writing format blob") // ensure storage is writable & has free quota
} Defensive patterns
Strategy: try-catch
Validate before calling
// Go: probe write access before repair
probeID := blob.ID("kopia-repair-write-probe")
if err := st.PutBlob(ctx, probeID, gather.FromSlice([]byte{1}), blob.PutOptions{}); err != nil {
return fmt.Errorf("storage not writable, aborting repair: %w", err)
}
_ = st.DeleteBlob(ctx, probeID) Try / catch
if puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {
return errors.Wrap(puterr, "error writing format blob") // check IAM write perms, quota, read-only mount
} Prevention
- Run repair with write-capable credentials (not read-only IAM policies).
- Check bucket quota and disk free space before repair.
- Avoid repairing onto read-only mounted filesystem paths.
- Use --dry-run to locate replicas before the write attempt.
When it happens
Trigger: PutBlob to format.KopiaRepositoryBlobID fails — storage backend I/O error, permission denied on the bucket/container, read-only storage mount, quota exceeded, or provider transient failure — while repairing without --dry-run.
Common situations: Repairing a repository whose storage credentials allow read but not write; a full S3 bucket or disk; a read-only mounted filesystem path; transient cloud-provider 5xx during repair.
Related errors
- unexpected error when listing blobs
- error advancing epoch
- error downloading indexes
- error getting committed state
- error loading index blob
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/13659eec5d18d55b.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command_repository_repair.go:92
prefixes := c.repairCommandRecoverFormatBlobPrefixes
if len(prefixes) == 0 {
prefixes = packBlockPrefixes()
}
return c.recoverFormatBlob(ctx, st, prefixes)
}
func (c *commandRepositoryRepair) recoverFormatBlob(ctx context.Context, st blob.Storage, prefixes []string) error {
errSuccess := errors.New("success")
for _, prefix := range prefixes {
err := st.ListBlobs(ctx, blob.ID(prefix), func(bi blob.Metadata) error {
log(ctx).Infof("looking for replica of format blob in %v...", bi.BlobID)
if b, err := format.RecoverFormatBlob(ctx, st, bi.BlobID, bi.Length); err == nil {
if !c.repairDryRun {
if puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {
return errors.Wrap(puterr, "error writing format blob")
}
}
log(ctx).Infof("recovered replica block from %v", bi.BlobID)
return errSuccess
}
return nil
})
switch {
case err == nil:
// do nothing
case errors.Is(err, errSuccess):
return nil
default:
return errors.Wrap(err, "unexpected error when listing blobs")View on GitHub (pinned to 82495e54b5)