Skip to content

Commit 02f7950

Browse files
committed
fix(core/node): unwrap fx error in node construction
1 parent 91c5265 commit 02f7950

File tree

3 files changed

+49
-3
lines changed

3 files changed

+49
-3
lines changed

cmd/ipfs/daemon.go

-1
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,6 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment
427427

428428
node, err := core.NewNode(req.Context, ncfg)
429429
if err != nil {
430-
log.Error("error from node construction: ", err)
431430
return err
432431
}
433432
node.IsDaemon = true

core/builder.go

+48-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package core
22

33
import (
44
"context"
5+
"fmt"
6+
"reflect"
57
"sync"
68
"time"
79

810
"github.com/ipfs/go-ipfs/core/bootstrap"
911
"github.com/ipfs/go-ipfs/core/node"
1012

1113
"github.com/ipfs/go-metrics-interface"
14+
"go.uber.org/dig"
1215
"go.uber.org/fx"
1316
)
1417

@@ -75,11 +78,11 @@ func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) {
7578
}()
7679

7780
if app.Err() != nil {
78-
return nil, app.Err()
81+
return nil, logAndUnwrapFxError(app.Err())
7982
}
8083

8184
if err := app.Start(ctx); err != nil {
82-
return nil, err
85+
return nil, logAndUnwrapFxError(err)
8386
}
8487

8588
// TODO: How soon will bootstrap move to libp2p?
@@ -89,3 +92,46 @@ func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) {
8992

9093
return n, n.Bootstrap(bootstrap.DefaultBootstrapConfig)
9194
}
95+
96+
// Log the entire `app.Err()` but return only the innermost one to the user
97+
// given the full error can be very long (as it can expose the entire build
98+
// graph in a single string).
99+
//
100+
// The fx.App error exposed through `app.Err()` normally contains un-exported
101+
// errors from its low-level `dig` package:
102+
// * https://github.com/uber-go/dig/blob/5e5a20d/error.go#L82
103+
// These usually wrap themselves in many layers to expose where in the build
104+
// chain did the error happen. Although useful for a developer that needs to
105+
// debug it, it can be very confusing for a user that just wants the IPFS error
106+
// that he can probably fix without being aware of the entire chain.
107+
// Unwrapping everything is not the best solution as there can be useful
108+
// information in the intermediate errors, mainly in the next to last error
109+
// that locates which component is the build error coming from, but it's the
110+
// best we can do at the moment given all errors in dig are private and we
111+
// just have the generic `RootCause` API.
112+
func logAndUnwrapFxError(fxAppErr error) error {
113+
if fxAppErr == nil {
114+
return nil
115+
}
116+
117+
log.Error("constructing the node: ", fxAppErr)
118+
119+
err := fxAppErr
120+
for {
121+
extractedErr := dig.RootCause(err)
122+
// Note that the `RootCause` name is misleading as it just unwraps only
123+
// *one* error layer at a time, so we need to continuously call it.
124+
if !reflect.TypeOf(extractedErr).Comparable() {
125+
// Some internal errors are not comparable (e.g., `dig.errMissingTypes`
126+
// which is a slice) and we can't go further.
127+
break
128+
}
129+
if extractedErr == err {
130+
// We didn't unwrap any new error in the last call, reached the innermost one.
131+
break
132+
}
133+
err = extractedErr
134+
}
135+
136+
return fmt.Errorf("constructing the node (see log for full detail): %w", err)
137+
}

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ require (
104104
github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1
105105
github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
106106
go.opencensus.io v0.23.0
107+
go.uber.org/dig v1.12.0
107108
go.uber.org/fx v1.15.0
108109
go.uber.org/zap v1.19.1
109110
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519

0 commit comments

Comments
 (0)