{"record":{"id":"5932e1c124c0f7e7","repo":"thanos-io/thanos","slug":"json-encode-deletion-mark","errorCode":null,"errorMessage":"json encode deletion mark","messagePattern":"json encode deletion mark","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/block/block.go","lineNumber":186,"sourceCode":"func MarkForDeletion(ctx context.Context, logger log.Logger, bkt objstore.Bucket, id ulid.ULID, details string, markedForDeletion prometheus.Counter) error {\n\tdeletionMarkFile := path.Join(id.String(), metadata.DeletionMarkFilename)\n\tdeletionMarkExists, err := bkt.Exists(ctx, deletionMarkFile)\n\tif err != nil {\n\t\treturn errors.Wrapf(err, \"check exists %s in bucket\", deletionMarkFile)\n\t}\n\tif deletionMarkExists {\n\t\tlevel.Warn(logger).Log(\"msg\", \"requested to mark for deletion, but file already exists; this should not happen; investigate\", \"err\", errors.Errorf(\"file %s already exists in bucket\", deletionMarkFile))\n\t\treturn nil\n\t}\n\n\tdeletionMark, err := json.Marshal(metadata.DeletionMark{\n\t\tID:           id,\n\t\tDeletionTime: time.Now().Unix(),\n\t\tVersion:      metadata.DeletionMarkVersion1,\n\t\tDetails:      details,\n\t})\n\tif err != nil {\n\t\treturn errors.Wrap(err, \"json encode deletion mark\")\n\t}\n\n\tif err := bkt.Upload(ctx, deletionMarkFile, bytes.NewBuffer(deletionMark)); err != nil {\n\t\treturn errors.Wrapf(err, \"upload file %s to bucket\", deletionMarkFile)\n\t}\n\tmarkedForDeletion.Inc()\n\tlevel.Info(logger).Log(\"msg\", \"block has been marked for deletion\", \"block\", id)\n\treturn nil\n}\n\n// Delete removes directory that is meant to be block directory.\n// NOTE: Always prefer this method for deleting blocks.\n//   - We have to delete block's files in the certain order (meta.json first and deletion-mark.json last)\n//     to ensure we don't end up with malformed partial blocks. Thanos system handles well partial blocks\n//     only if they don't have meta.json. If meta.json is present Thanos assumes valid block.\n//   - This avoids deleting empty dir (whole bucket) by mistake.\nfunc Delete(ctx context.Context, logger log.Logger, bkt objstore.Bucket, id ulid.ULID) error {\n\tmetaFile := path.Join(id.String(), MetaFilename)","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/block/block.go#L168-L204","documentation":"MarkForDeletion serializes a deletion-mark.json object (block ID, timestamp, details) to JSON before uploading it to the bucket. This error wraps a failure of json.Marshal on that structure, which practically never fails unless the encoder is misused.","triggerScenarios":"json.Marshal returning an error while encoding metadata.DeletionMark in MarkForDeletion — essentially only possible via unsupported types or custom marshaling bugs in the DeletionMark struct.","commonSituations":"Custom forks that added unmarshalable fields (e.g. channels, funcs) to DeletionMark; corrupted vendored/patched versions of the metadata package.","solutions":["Use stock metadata.DeletionMark struct; remove any custom field that cannot be JSON-marshaled","Inspect the wrapped error for the marshal failure detail and fix the offending field","Upgrade to an unmodified Thanos release"],"exampleFix":"// before\ntype DeletionMark struct { Done chan struct{} `json:\"done\"` }\n// after\ntype DeletionMark struct { ID ulid.ULID; DeletionTime int64; Version int }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := block.MarkForDeletion(ctx, logger, bkt, id, details); err != nil {\n    if strings.Contains(err.Error(), \"json encode deletion mark\") {\n        return fmt.Errorf(\"deletion mark serialization failed (check custom metadata structs): %w\", err)\n    }\n    return err\n}","preventionTips":["Do not add unmarshalable fields to metadata.DeletionMark","Test MarkForDeletion in CI with the exact binary you deploy"],"tags":["json","serialization","internal"],"backgroundTag":"json-marshal-failed","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}