{"record":{"id":"13659eec5d18d55b","repo":"kopia/kopia","slug":"error-writing-format-blob","errorCode":null,"errorMessage":"error writing format blob","messagePattern":"error writing format blob","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/command_repository_repair.go","lineNumber":92,"sourceCode":"\tprefixes := c.repairCommandRecoverFormatBlobPrefixes\n\tif len(prefixes) == 0 {\n\t\tprefixes = packBlockPrefixes()\n\t}\n\n\treturn c.recoverFormatBlob(ctx, st, prefixes)\n}\n\nfunc (c *commandRepositoryRepair) recoverFormatBlob(ctx context.Context, st blob.Storage, prefixes []string) error {\n\terrSuccess := errors.New(\"success\")\n\n\tfor _, prefix := range prefixes {\n\t\terr := st.ListBlobs(ctx, blob.ID(prefix), func(bi blob.Metadata) error {\n\t\t\tlog(ctx).Infof(\"looking for replica of format blob in %v...\", bi.BlobID)\n\n\t\t\tif b, err := format.RecoverFormatBlob(ctx, st, bi.BlobID, bi.Length); err == nil {\n\t\t\t\tif !c.repairDryRun {\n\t\t\t\t\tif puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {\n\t\t\t\t\t\treturn errors.Wrap(puterr, \"error writing format blob\")\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlog(ctx).Infof(\"recovered replica block from %v\", bi.BlobID)\n\n\t\t\t\treturn errSuccess\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\n\t\tswitch {\n\t\tcase err == nil:\n\t\t\t// do nothing\n\t\tcase errors.Is(err, errSuccess):\n\t\t\treturn nil\n\t\tdefault:\n\t\t\treturn errors.Wrap(err, \"unexpected error when listing blobs\")","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/cli/command_repository_repair.go#L74-L110","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {\n\treturn errors.Wrap(puterr, \"error writing format blob\")\n}\n// after\nif puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {\n\treturn errors.Wrap(puterr, \"error writing format blob\") // ensure storage is writable & has free quota\n}","handlingStrategy":"try-catch","validationCode":"// Go: probe write access before repair\nprobeID := blob.ID(\"kopia-repair-write-probe\")\nif err := st.PutBlob(ctx, probeID, gather.FromSlice([]byte{1}), blob.PutOptions{}); err != nil {\n\treturn fmt.Errorf(\"storage not writable, aborting repair: %w\", err)\n}\n_ = st.DeleteBlob(ctx, probeID)","typeGuard":null,"tryCatchPattern":"if puterr := st.PutBlob(ctx, format.KopiaRepositoryBlobID, gather.FromSlice(b), blob.PutOptions{}); puterr != nil {\n\treturn errors.Wrap(puterr, \"error writing format blob\") // check IAM write perms, quota, read-only mount\n}","preventionTips":["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."],"tags":["kopia","blob-storage","write-failure","repair"],"backgroundTag":"blob-write-failed","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}