This guide is intended for developers working on the Dapr code base, specifically for those updating the error handling to align with the gRPC richer error model. The goal is to standardize error responses across Dapr's services.
- Familiarity with gRPC and its error model. Refer to the gRPC Richer Error Model Guide.
Currently, error handling in Dapr is a mix of predefined errors and dynamically constructed errors within the code.
- Predefined Errors: There are some legacy predefined errors located at
/pkg/messages/predefined.go
, but the new errors are located in/pkg/api/errors
. These errors are standard and reused across various parts of the Dapr codebase. They provide a consistent error handling mechanism for common scenarios. - Dynamically Constructed Errors: These errors are created on the fly within the code, typically to handle specific situations or errors that are not covered by the predefined set.
As we move predefined errors to the rich error model and a new location (pkg/api/errors
), the first step in updating to the new error model is to familiarize yourself with the existing error handling patterns, both predefined and dynamically constructed. This understanding will be crucial when replacing them with the richer error model aligned with gRPC standards.
- Check for existing errors: Before creating a new error, check if a relevant error file exists in
/pkg/api/errors/
. - Create a new error file: If no relevant file exists, create a new file following the pattern
<building-block>.go
.
- Understand Rich Error Construction: Familiarize yourself with the construction of rich error messages. The definition can be found in
github.com/dapr/kit/errors
. - Helper Methods: Utilize the error builder and the helper methods like
WithErrorInfo
,WithResourceInfo
to enrich error information. - Reference implementation: You can check out
pkg/api/errors/state.go
for a reference implementation. The following code snippet shows how to create a new error using the helper methods:
func (s *StateStoreError) InvalidKeyName(key string, msg string) error {
return s.build(
errors.NewBuilder(
codes.InvalidArgument,
http.StatusBadRequest,
msg,
"ERR_MALFORMED_REQUEST",
).WithFieldViolation(key, msg),
errors.CodeIllegalKey,
nil,
)
}
- Mandatory and Optional Information:
- ErrorInfo: This is a required field.
- ResourceInfo and other Details fields: These are optional but should be used following best practices as outlined in the Google Cloud Error Model.
- Consistent Use: Ensure that the new error model is used consistently throughout the codebase.
- Refactoring: Replace existing errors in the code with the newly defined rich errors.
- Integration Tests: Add integration tests for the new error handling mechanism.
- Follow Existing Patterns: Use the existing integration tests for the state API as a reference for structure and best practices. For example, these are the integration tests for the errors in state api:
/tests/integration/suite/daprd/state/grpc/errors.go
and/tests/integration/suite/daprd/state/http/errors.go