How FCM reports errors
A failed FCM HTTP v1 request returns a JSON body shaped like {"error":{"code":400,"message":"...","status":"INVALID_ARGUMENT"}}. The status field is the reliable part to check in code; the numeric code is the matching HTTP status, and message is a human-readable detail that can vary.
UNAUTHENTICATED
Firebase could not verify who is making the request. This almost always means the OAuth access token used to authorize the send is missing, expired, or was minted from the wrong service account. Re-check that the service account JSON is the current one downloaded from Firebase, not an older or revoked key.
PERMISSION_DENIED
The request was authenticated, but that service account is not authorized to send FCM messages for the target project. This happens when a service account from a different Firebase project is used, or when its role does not include Firebase Cloud Messaging send access (the default Firebase Admin SDK service account normally has this by default).
NOT_FOUND
Firebase could not find the project or the registration referenced in the request. Confirm the project_id in the service account JSON matches the Firebase project that issued the device token you are sending to.
INVALID_ARGUMENT
The message payload itself was rejected: a malformed device token, an invalid field in the JSON body, or a value FCM does not accept (for example, a non-string value inside data). The error message text usually names the offending field.
UNREGISTERED
The device token is syntactically valid but no longer registered with FCM. The app may have been uninstalled, the token may have been refreshed on the client, or it may have expired. Get a fresh token from the client SDK and try again.
QUOTA_EXCEEDED
The sending rate for that project or target device exceeded FCM's limits. This is uncommon during manual testing; it usually points to a send loop or a very high-volume production integration.
UNAVAILABLE and INTERNAL
A transient problem on Google's side. These are safe to retry with backoff and are not caused by your request.
You can reproduce any of these against a real project using the FCM notification tester — the exact error status and message are shown in the result panel after a failed send.