{"record":{"id":"d93f4bee940677cf","repo":"containerd/containerd","slug":"truncate-failed-w","errorCode":null,"errorMessage":"truncate failed: %w","messagePattern":"truncate failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/services/content/contentserver/contentserver.go","lineNumber":391,"sourceCode":"\t\t}\n\n\t\tswitch req.Action {\n\t\tcase api.WriteAction_STAT:\n\t\t\tmsg.Digest = wr.Digest().String()\n\t\t\tmsg.StartedAt = protobuf.ToTimestamp(ws.StartedAt)\n\t\t\tmsg.UpdatedAt = protobuf.ToTimestamp(ws.UpdatedAt)\n\t\t\tmsg.Total = total\n\t\tcase api.WriteAction_WRITE, api.WriteAction_COMMIT:\n\t\t\tif req.Offset > 0 {\n\t\t\t\t// validate the offset if provided\n\t\t\t\tif req.Offset != ws.Offset {\n\t\t\t\t\treturn status.Errorf(codes.OutOfRange, \"write @%v must occur at current offset %v\", req.Offset, ws.Offset)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif req.Offset == 0 && ws.Offset > 0 {\n\t\t\t\tif err := wr.Truncate(req.Offset); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"truncate failed: %w\", err)\n\t\t\t\t}\n\t\t\t\tmsg.Offset = req.Offset\n\t\t\t}\n\n\t\t\t// issue the write if we actually have data.\n\t\t\tif len(req.Data) > 0 {\n\t\t\t\t// While this looks like we could use io.WriterAt here, because we\n\t\t\t\t// maintain the offset as append only, we just issue the write.\n\t\t\t\tn, err := wr.Write(req.Data)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errgrpc.ToGRPC(err)\n\t\t\t\t}\n\n\t\t\t\tif n != len(req.Data) {\n\t\t\t\t\t// TODO(stevvooe): Perhaps, we can recover this by including it\n\t\t\t\t\t// in the offset on the write return.\n\t\t\t\t\treturn status.Errorf(codes.DataLoss, \"wrote %v of %v bytes\", n, len(req.Data))\n\t\t\t\t}","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/containerd/containerd/blob/4246446a2bf7d03837b0244118d858799393bd80/plugins/services/content/contentserver/contentserver.go#L373-L409","documentation":"The content service's Write RPC failed to truncate the underlying writer back to the requested offset. This happens only when a client restarts a write at offset 0 after having already written data, and the writer's Truncate call fails (I/O or state error).","triggerScenarios":"Calling the content Write stream with Offset==0 while the server-side writer status shows Offset>0, and wr.Truncate(0) fails — e.g. the writer is no longer truncatable or the backing file I/O errors.","commonSituations":"A client restarting an interrupted blob upload from scratch while a previous partial write exists; disk full or read-only filesystem during truncate; concurrent writers on the same blob ref.","solutions":["Retry the write after the underlying I/O condition (disk space, read-only fs) is fixed","Abort the existing write session (content Abort) and start a fresh Write","Ensure only one writer is active per ref to avoid conflicting offsets","Update the client to resume from the server-reported offset instead of restarting at 0"],"exampleFix":"// before: client restarts upload at 0 after partial write\nreq := &contentapi.WriteRequest{Ref: ref, Offset: 0, Data: data}\n// after: resume from server status or abort first\n// st, _ := client.Status(ctx, ref); if st.Offset > 0 { client.Abort(ctx, ref) }","handlingStrategy":"try-catch","validationCode":"// client: check server-side offset before restarting at 0\nst, err := client.Status(ctx, ref)\nif err == nil && st.Offset > 0 { /* resume instead of Offset:0 */ }","typeGuard":null,"tryCatchPattern":"err := stream.Send(req); ...\nif err != nil && strings.Contains(err.Error(), \"truncate failed\") {\n    client.Abort(ctx, ref)\n    return startFreshWrite(ctx, ref, data)\n}","preventionTips":["Resume uploads from the server-reported offset rather than restarting at 0","Abort stale write sessions before starting a new one on the same ref","Ensure adequate disk space and writable filesystem for the content store","Serialize writers per ref"],"tags":["content-store","grpc","io","containerd"],"backgroundTag":"truncate-failed","analyzedSha":"4246446a2bf7d03837b0244118d858799393bd80","analyzedAt":"2026-09-02T00:14:43.053Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}