-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Returns structured errors from FundPsbt #5436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
4370424
to
ea550e1
Compare
Converts errors likely to be thrown from string based errors to error classes with returned grpc error codes
ea550e1
to
4a7356f
Compare
lnrpc/walletrpc/psbt.go
Outdated
@@ -36,8 +38,7 @@ func verifyInputsUnspent(inputs []*wire.TxIn, utxos []*lnwallet.Utxo) error { | |||
} | |||
|
|||
if !found { | |||
return fmt.Errorf("input %d not found in list of non-"+ | |||
"locked UTXO", idx) | |||
return status.Error(codes.NotFound, ErrUnspentInputNotFound(idx).Error()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will return a structured error to the client and they still need to parse the text message to find out the index?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it depends what you mean by a structured error. On the client side, they can introspect the error and extract the error code used and act upon then, possibly doing string parsing if they need any additional context.
Unless you mean return a response that has fields to enumerate the different types of errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did some digging in the docs, and looks like it's possible for us to actually get the best of both worlds here: https://pkg.go.dev/google.golang.org/grpc/internal/status?utm_source=godoc#Status.WithDetails
This API lets you make a status code to return using status.Error
, but then also attach an arbitrary proto message that can be used to let the client optionally get more structured information if it needs to. This blog post has a good overview of how things would work end to end: https://jbrandhorst.com/post/grpc-errors/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, WithDetails
is what I used before indeed for that. I think that can come in useful in various places, because not everything maps cleanly to a grpc code. And in this case, the index needs to be stored somewhere. If an utxo is locked, it is likely that a client needs info about which one it is exactly to do another funding attempt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the response provide key value information per error type (if index not found, return missing index via key) or should we go with more generic error codes with arbitrary information? The issue of having systems respond appropriately to failures is addressed with the generic error codes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If think the requirement here is that systems should be able to respond appropriately to a specific utxo being unavailable. Just a generic error code wouldn't suffice then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know about the WithDetails
, could be useful indeed. Though the fact that it needs to be a proto message would mean we'd need to add new messages to our main proto file just for structured errors? Might get bloated quite quickly.
Also, I wonder how such an error message (with details) would look on the command line, if the error is just printed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A problem to consider would also be the scope of structured errors, and the lack of unity between different error cases. FundPSBT
has many unique error cases, while only 2 in particular are covered by this ticket.
@guggero the error should be printed in json to the client imo, as the purpose is for machine consumption.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There may be many unique error cases, but all the 'unexpected' ones - the internal errors - don't need to be returned in a structured way I'd say. I am not sure if that many structured errors remain.
e0042ca
to
92c8c0d
Compare
Using status.WithDetails, we attach more metadata which is then printed as json in certain cases for machine consumption. Added the errdetails package to provide more robust error coding
92c8c0d
to
a842fcd
Compare
This commit compiles the grpc protos since additional output is added to the FundPSBT command
Adds a test to ensure errors are thrown when utxos with incorrect indices are provided to FundPSBT.
I think the idea of starting to return more useful information with errors is a really good one! There are a few things I personally don't like about the current approach:
But I think the ideas in this PR are good and should be taken into account.
What do you think, @arshbot, @joostjager ? |
If error details are a string, is that structured error details? |
I would argue yes. You get key/value pairs with known and stable keys and you don't have to parse a string to get to them. Yes, you might need to convert the string value into a native data type. But at least this would be very generic and could still be shown as a human readable error string to the user. But this is just my idea for making things more generic, I'm open to suggestions if you feel there's another way. |
I think that ideally you don't have a generic error code, not even if it is per domain. Because with a generic code, you still need to go through the lnd source code to see what the exact error codes are that need to be handled for a specific call, or rely on documentation that can get desynced with the code relatively easy. Using a specific proto error object gets you the strictest contract. It doesn't need to be one of the predefined google messages that is attached with I do see the point of ease of use. Just checking the top-level grpc code is very convenient. |
I think the existing model has its strength in surfacing the documentation of known common failure states to watch out for, but I agree the weakness is having unknown or uncommon errors return a result that is hard to rely on and also it's kind of unreasonable to expect enumeration of every possible error in the gRPC. For common failures I like the proto structured failure responses, for uncommon errors I like the current model of strings explaining what happened but I think the proposed schema for a number system wouldn't help me much with knowing what to do in response to that error. The main weakness of the structured responses is that sometimes the real underlying failure doesn't really match up to what is reported in the structure, but that could be resolved by adding more enumerations or not being so strict with trying to return a structured error in unexpected cases. In HTTP the error codes classes of errors also have prescriptions of what to do: in a 4xx class you generally need to fix your own problem and in a 5xx class you need to retry or wait for the server to fix their problem, and within those codes there are specific prescriptions for behavior. That pattern is hard to replicate here so I'm not sure it can be copied. I'd definitely like to have a 'unique identifier' for an error though which is basically just replacing the string that I'm currently matching for with some value that the RPC says won't change and then the RPC would be more free to change the strings that I'm matching against for typos or adding context etc. It's especially difficult to do string matching when the error string is adding in contextual details, then I have to do regex matching. |
@arshbot any thoughts on the discussion above? Should we try to pick this up again for 0.15? |
I would take this issue and finish it, is this ok? I saw its |
Yes, feel free to start working on this. But just a heads up, I don't think we actually came to a conclusion on how exactly we'd like to structure the error codes (see discussion above), so it's possible there might be quite a bit of back and forth during the review. |
not working on this currently, have found another more urgent issue for now, will come back in the future! |
@arshbot, remember to re-request review from reviewers when ready |
Closing due to inactivity |
12 similar comments
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Closing due to inactivity |
Converts errors likely to be thrown from string based errors to error
classes with returned grpc error codes. Addresses #5411
Pull Request Checklist
Contribution Guidelines
the positive and negative (error paths) conditions (if applicable)
the bug being fixed to prevent regressions
logging level
go fmt
lnrpc/**/*.proto
) have been formatted withmake rpc-format
and compiled withmake rpc
sample-lnd.conf
(the tab character should be counted as 8 characters, not 4, as some IDEs do
per default)
make check
does not fail any testsgo vet
does not report any issuesmake lint
does not report any new issues that did notalready exist
cases it can be justifiable to violate this condition. In that case, the
reason should be stated in the commit message.