Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
54b8114
Merge dev into master
google-oss-bot May 21, 2020
cef91ac
Merge dev into master
google-oss-bot Jun 16, 2020
77177c7
Merge dev into master
google-oss-bot Oct 22, 2020
a957589
Merge dev into master
google-oss-bot Jan 28, 2021
eb0d2a0
Merge dev into master
google-oss-bot Mar 24, 2021
05378ef
Merge dev into master
google-oss-bot Mar 29, 2021
4121c50
Merge dev into master
google-oss-bot Apr 14, 2021
928b104
Merge dev into master
google-oss-bot Jun 2, 2021
02cde4f
Merge dev into master
google-oss-bot Nov 4, 2021
6b40682
Merge dev into master
google-oss-bot Dec 15, 2021
e60757f
Merge dev into master
google-oss-bot Jan 20, 2022
bb055ed
Merge dev into master
google-oss-bot Apr 6, 2022
23a1f17
Merge dev into master
google-oss-bot Oct 6, 2022
1d24577
Merge dev into master
google-oss-bot Nov 10, 2022
61c6c04
Merge dev into master
google-oss-bot Apr 6, 2023
32af2b8
[chore] Release 4.12.0 (#561)
lahirumaramba Jun 22, 2023
02300a8
Revert "[chore] Release 4.12.0 (#561)" (#565)
lahirumaramba Jul 11, 2023
74c9bd5
Merge dev into master
google-oss-bot Jul 12, 2023
37c7936
Merge dev into master
google-oss-bot Sep 25, 2023
b04387e
Merge dev into master
google-oss-bot Nov 23, 2023
87b867c
Merge dev into master
google-oss-bot Apr 10, 2024
6a28190
Merge dev into master
google-oss-bot May 30, 2024
c3be6f2
Merge dev into master
google-oss-bot Oct 24, 2024
afeaa15
Merge dev into master
google-oss-bot Dec 5, 2024
570427a
Merge dev into master
google-oss-bot Feb 13, 2025
fe866a0
Merge dev into master
google-oss-bot Jun 5, 2025
db240e4
Merge dev into master
google-oss-bot Jun 11, 2025
d515faf
Merge dev into master
google-oss-bot Jul 17, 2025
26dec0b
Merge dev into master
google-oss-bot Jul 31, 2025
5565b15
feat: expose Firebase error information to SDK users
putcho01 Nov 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 227 additions & 0 deletions errorutils/errorutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,233 @@ import (
"firebase.google.com/go/v4/internal"
)

// ErrorCode represents the platform-wide error codes that can be raised by Firebase Admin SDK APIs.
type ErrorCode string

const (
// InvalidArgument indicates the request was invalid or malformed.
InvalidArgument ErrorCode = "INVALID_ARGUMENT"

// FailedPrecondition indicates the request could not be executed in the current system state.
FailedPrecondition ErrorCode = "FAILED_PRECONDITION"

// OutOfRange indicates an invalid range was specified by the client.
OutOfRange ErrorCode = "OUT_OF_RANGE"

// Unauthenticated indicates the request lacks valid authentication credentials.
Unauthenticated ErrorCode = "UNAUTHENTICATED"

// PermissionDenied indicates the client does not have sufficient permission.
PermissionDenied ErrorCode = "PERMISSION_DENIED"

// NotFound indicates the specified resource was not found.
NotFound ErrorCode = "NOT_FOUND"

// Conflict indicates a conflict occurred (e.g., concurrent modification).
Conflict ErrorCode = "CONFLICT"

// Aborted indicates the operation was aborted (e.g., transaction conflict).
Aborted ErrorCode = "ABORTED"

// AlreadyExists indicates the resource already exists.
AlreadyExists ErrorCode = "ALREADY_EXISTS"

// ResourceExhausted indicates a quota or rate limit was exceeded.
ResourceExhausted ErrorCode = "RESOURCE_EXHAUSTED"

// Cancelled indicates the operation was cancelled by the client.
Cancelled ErrorCode = "CANCELLED"

// DataLoss indicates unrecoverable data loss or corruption.
DataLoss ErrorCode = "DATA_LOSS"

// Unknown indicates an unknown server error.
Unknown ErrorCode = "UNKNOWN"

// Internal indicates an internal server error.
Internal ErrorCode = "INTERNAL"

// Unavailable indicates the service is currently unavailable.
Unavailable ErrorCode = "UNAVAILABLE"

// DeadlineExceeded indicates the request exceeded the deadline.
DeadlineExceeded ErrorCode = "DEADLINE_EXCEEDED"
)

// MessagingErrorCode represents FCM-specific error codes.
type MessagingErrorCode string

const (
// MessagingAPNSAuthError indicates an error with APNS authentication.
MessagingAPNSAuthError MessagingErrorCode = "APNS_AUTH_ERROR"

// MessagingInternal indicates an internal messaging service error.
MessagingInternal MessagingErrorCode = "INTERNAL"

// MessagingThirdPartyAuthError indicates an error with third-party authentication.
MessagingThirdPartyAuthError MessagingErrorCode = "THIRD_PARTY_AUTH_ERROR"

// MessagingInvalidArgument indicates an invalid messaging argument.
MessagingInvalidArgument MessagingErrorCode = "INVALID_ARGUMENT"

// MessagingQuotaExceeded indicates the messaging quota was exceeded.
MessagingQuotaExceeded MessagingErrorCode = "QUOTA_EXCEEDED"

// MessagingSenderIDMismatch indicates a sender ID mismatch.
MessagingSenderIDMismatch MessagingErrorCode = "SENDER_ID_MISMATCH"

// MessagingUnregistered indicates the device token is no longer valid.
MessagingUnregistered MessagingErrorCode = "UNREGISTERED"

// MessagingUnavailable indicates the messaging service is unavailable.
MessagingUnavailable MessagingErrorCode = "UNAVAILABLE"
)

// AuthErrorCode represents Auth-specific error codes.
type AuthErrorCode string

const (
// AuthConfigurationNotFound indicates the configuration was not found.
AuthConfigurationNotFound AuthErrorCode = "CONFIGURATION_NOT_FOUND"

// AuthEmailAlreadyExists indicates the email address is already in use.
AuthEmailAlreadyExists AuthErrorCode = "EMAIL_ALREADY_EXISTS"

// AuthEmailNotFound indicates no user record found for the email.
AuthEmailNotFound AuthErrorCode = "EMAIL_NOT_FOUND"

// AuthInvalidDynamicLinkDomain indicates an invalid dynamic link domain.
AuthInvalidDynamicLinkDomain AuthErrorCode = "INVALID_DYNAMIC_LINK_DOMAIN"

// AuthInvalidEmail indicates the email address is invalid.
AuthInvalidEmail AuthErrorCode = "INVALID_EMAIL"

// AuthInvalidPageToken indicates the page token is invalid.
AuthInvalidPageToken AuthErrorCode = "INVALID_PAGE_TOKEN"

// AuthPhoneNumberAlreadyExists indicates the phone number is already in use.
AuthPhoneNumberAlreadyExists AuthErrorCode = "PHONE_NUMBER_ALREADY_EXISTS"

// AuthProjectNotFound indicates the project was not found.
AuthProjectNotFound AuthErrorCode = "PROJECT_NOT_FOUND"

// AuthUIDAlreadyExists indicates the UID is already in use.
AuthUIDAlreadyExists AuthErrorCode = "UID_ALREADY_EXISTS"

// AuthUnauthorizedContinueURL indicates an unauthorized continue URL.
AuthUnauthorizedContinueURL AuthErrorCode = "UNAUTHORIZED_CONTINUE_URL"

// AuthUserNotFound indicates no user record found for the identifier.
AuthUserNotFound AuthErrorCode = "USER_NOT_FOUND"

// AuthIDTokenExpired indicates the ID token has expired.
AuthIDTokenExpired AuthErrorCode = "ID_TOKEN_EXPIRED"

// AuthIDTokenInvalid indicates the ID token is invalid.
AuthIDTokenInvalid AuthErrorCode = "ID_TOKEN_INVALID"

// AuthIDTokenRevoked indicates the ID token has been revoked.
AuthIDTokenRevoked AuthErrorCode = "ID_TOKEN_REVOKED"

// AuthSessionCookieExpired indicates the session cookie has expired.
AuthSessionCookieExpired AuthErrorCode = "SESSION_COOKIE_EXPIRED"

// AuthSessionCookieInvalid indicates the session cookie is invalid.
AuthSessionCookieInvalid AuthErrorCode = "SESSION_COOKIE_INVALID"

// AuthSessionCookieRevoked indicates the session cookie has been revoked.
AuthSessionCookieRevoked AuthErrorCode = "SESSION_COOKIE_REVOKED"

// AuthUserDisabled indicates the user account has been disabled.
AuthUserDisabled AuthErrorCode = "USER_DISABLED"

// AuthTenantIDMismatch indicates a tenant ID mismatch.
AuthTenantIDMismatch AuthErrorCode = "TENANT_ID_MISMATCH"

// AuthCertificateFetchFailed indicates a failure to fetch certificates.
AuthCertificateFetchFailed AuthErrorCode = "CERTIFICATE_FETCH_FAILED"

// AuthInsufficientPermission indicates insufficient permission.
AuthInsufficientPermission AuthErrorCode = "INSUFFICIENT_PERMISSION"

// AuthTenantNotFound indicates the tenant was not found.
AuthTenantNotFound AuthErrorCode = "TENANT_NOT_FOUND"
)

// FirebaseError represents an error returned by a Firebase service.
// It provides detailed information about platform-wide errors and service-specific error codes.
type FirebaseError struct {
// ErrorCode is the platform-wide error code (e.g., INVALID_ARGUMENT, NOT_FOUND).
ErrorCode ErrorCode

// Message is the human-readable error message.
Message string

// Response is the HTTP response that caused the error (buffered copy).
// This may be nil for errors not caused by HTTP responses.
Response *http.Response

// MessagingErrorCode is the FCM-specific error code, if applicable.
// Empty string if this is not a messaging error.
MessagingErrorCode MessagingErrorCode

// AuthErrorCode is the Auth-specific error code, if applicable.
// Empty string if this is not an auth error.
AuthErrorCode AuthErrorCode
}

// Error implements the error interface.
func (e *FirebaseError) Error() string {
return e.Message
}

// AsFirebaseError converts an error to a *FirebaseError if it is a Firebase error.
// Returns nil if the error is not a Firebase error.
//
// This function allows SDK users to access detailed error information including
// platform error codes, service-specific error codes, and HTTP response details.
//
// Example usage:
//
// _, err := client.Send(ctx, message)
// if fbErr := errorutils.AsFirebaseError(err); fbErr != nil {
// switch fbErr.ErrorCode {
// case errorutils.NotFound:
// // Handle not found error
// case errorutils.InvalidArgument:
// // Handle invalid argument error
// }
//
// // Access messaging-specific error codes
// if fbErr.MessagingErrorCode == errorutils.MessagingUnregistered {
// // Remove device token from database
// }
// }
func AsFirebaseError(err error) *FirebaseError {
fe, ok := err.(*internal.FirebaseError)
if !ok {
return nil
}

pubErr := &FirebaseError{
ErrorCode: ErrorCode(fe.ErrorCode),
Message: fe.String,
Response: fe.Response,
}

// Extract messaging-specific error code if present
if msgCode, ok := fe.Ext["messagingErrorCode"].(string); ok {
pubErr.MessagingErrorCode = MessagingErrorCode(msgCode)
}

// Extract auth-specific error code if present
if authCode, ok := fe.Ext["authErrorCode"].(string); ok {
pubErr.AuthErrorCode = AuthErrorCode(authCode)
}

return pubErr
}

// IsInvalidArgument checks if the given error was due to an invalid client argument.
func IsInvalidArgument(err error) bool {
return internal.HasPlatformErrorCode(err, internal.InvalidArgument)
Expand Down
Loading