Skip to content

Commit f5f6b66

Browse files
authored
fix(cmd): useful errors in dag import (#9945)
* fix: useful errors during dag import Most of the time the error is either a bitflip in one of blocks, or a truncation of car stream. This allows user to understand what happened and at which place in the car stream, making debug more humane. * fix: correct message when root pin failed this also correctly exits CLI commands with code 1 (was silent false-positive 0 before)
1 parent b8da86e commit f5f6b66

File tree

2 files changed

+22
-5
lines changed

2 files changed

+22
-5
lines changed

core/commands/dag/dag.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ Specification of CAR formats: https://ipld.io/specs/transport/car/
237237
}
238238

239239
if event.Root.PinErrorMsg != "" {
240-
event.Root.PinErrorMsg = fmt.Sprintf("FAILED: %s", event.Root.PinErrorMsg)
240+
return fmt.Errorf("pinning root %q FAILED: %s", enc.Encode(event.Root.Cid), event.Root.PinErrorMsg)
241241
} else {
242242
event.Root.PinErrorMsg = "success"
243243
}

core/commands/dag/import.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package dagcmd
22

33
import (
44
"errors"
5+
"fmt"
56
"io"
67

78
"github.com/ipfs/boxo/coreiface/options"
89
"github.com/ipfs/boxo/files"
910
gocarv2 "github.com/ipfs/boxo/ipld/car/v2"
11+
blocks "github.com/ipfs/go-block-format"
1012
cid "github.com/ipfs/go-cid"
1113
cmds "github.com/ipfs/go-ipfs-cmds"
1214
ipld "github.com/ipfs/go-ipld-format"
@@ -58,6 +60,18 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
5860
roots := cid.NewSet()
5961
var blockCount, blockBytesCount uint64
6062

63+
// remember last valid block and provide a meaningful error message
64+
// when a truncated/mangled CAR is being imported
65+
importError := func(previous blocks.Block, current blocks.Block, err error) error {
66+
if current != nil {
67+
return fmt.Errorf("import failed at block %q: %w", current.Cid(), err)
68+
}
69+
if previous != nil {
70+
return fmt.Errorf("import failed after block %q: %w", previous.Cid(), err)
71+
}
72+
return fmt.Errorf("import failed: %w", err)
73+
}
74+
6175
it := req.Files.Entries()
6276
for it.Next() {
6377
file := files.FileFromEntry(it)
@@ -75,6 +89,8 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
7589
// this won't/can't help with not running out of handles
7690
defer file.Close()
7791

92+
var previous blocks.Block
93+
7894
car, err := gocarv2.NewBlockReader(file)
7995
if err != nil {
8096
return err
@@ -87,25 +103,26 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
87103
for {
88104
block, err := car.Next()
89105
if err != nil && err != io.EOF {
90-
return err
106+
return importError(previous, block, err)
91107
} else if block == nil {
92108
break
93109
}
94110
if err := cmdutils.CheckBlockSize(req, uint64(len(block.RawData()))); err != nil {
95-
return err
111+
return importError(previous, block, err)
96112
}
97113

98114
// the double-decode is suboptimal, but we need it for batching
99115
nd, err := blockDecoder.DecodeNode(req.Context, block)
100116
if err != nil {
101-
return err
117+
return importError(previous, block, err)
102118
}
103119

104120
if err := batch.Add(req.Context, nd); err != nil {
105-
return err
121+
return importError(previous, block, err)
106122
}
107123
blockCount++
108124
blockBytesCount += uint64(len(block.RawData()))
125+
previous = block
109126
}
110127
return nil
111128
}()

0 commit comments

Comments
 (0)