@@ -2,13 +2,16 @@ package core
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
6
+ "reflect"
5
7
"sync"
6
8
"time"
7
9
8
10
"github.com/ipfs/go-ipfs/core/bootstrap"
9
11
"github.com/ipfs/go-ipfs/core/node"
10
12
11
13
"github.com/ipfs/go-metrics-interface"
14
+ "go.uber.org/dig"
12
15
"go.uber.org/fx"
13
16
)
14
17
@@ -75,11 +78,11 @@ func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) {
75
78
}()
76
79
77
80
if app .Err () != nil {
78
- return nil , app .Err ()
81
+ return nil , logAndUnwrapFxError ( app .Err () )
79
82
}
80
83
81
84
if err := app .Start (ctx ); err != nil {
82
- return nil , err
85
+ return nil , logAndUnwrapFxError ( err )
83
86
}
84
87
85
88
// TODO: How soon will bootstrap move to libp2p?
@@ -89,3 +92,46 @@ func NewNode(ctx context.Context, cfg *BuildCfg) (*IpfsNode, error) {
89
92
90
93
return n , n .Bootstrap (bootstrap .DefaultBootstrapConfig )
91
94
}
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
+ }
0 commit comments