Edit history

Earlier versions of Self-hosted native FCM (Android) push silently dropped - invalid `group` field in android.notification -> FCM HTTP 400, newest first.

Current version | Edited by Rex
Changes
- `push_fcm_payload:handle_response/3` returns `false` silently on any non-2xx (no log line).Removed: - The self-hosted gateway image emits **no logs at all**: `releases/*/sys.config` sets the default logger handler to `undefined`, and `docker_entrypoint.sh` runs `exec … foreground >/dev/null 2>&1` (see [#1144](https://feedback.fluxer.com/p/665)).Added: - The self-hosted gateway image emits **no logs at all**: `releases/*/sys.config` sets the default logger handler to `undefined`, and `docker_entrypoint.sh` runs `exec … foreground >/dev/null 2>&1` (see [#665](https://feedback.fluxer.com/p/665)).So the failure is completely silent end-to-end.### Reproduction1. Self-hosted (Docker Compose), Postgres backend, FCM configured (`FLUXER_PUSH_FCM_*`).2. Register a native Android FCM device; send it any message.3. Nothing arrives. A raw FCM v1 send **to the same device token** with the same payload shape reproduces it deterministically: with `android.notification.group` -> `HTTP 400`; remove it -> `HTTP 200` and the notification is delivered.### FixDrop the `group` field. `collapse_key` (set in `wrap_notification_message`, a valid `AndroidConfig` field) still handles collapsing:```erlangbuild_android_notification(Tag, ImageUrl) -> maybe_put(<<"image">>, ImageUrl, #{ <<"channel_id">> => <<"fluxer_default_push">>, <<"tag">> => Tag, <<"click_action">> => <<"FLUXER_MESSAGE">> }).```Verified on a live self-hosted instance: native Android DM/channel push now delivers, tap-through works. **iOS/APNs** (`ApnsPushService`) and **web-push/VAPID** use different payload builders and are unaffected by this field.Removed: ### Bonus - getting gateway logs (re [#1144](https://feedback.fluxer.com/p/665))Added: ### Bonus - getting gateway logs (re [#665](https://feedback.fluxer.com/p/665))Because the image discards all logs, you can attach a file logger to the running node at runtime via the release's RPC (no restart, fully reversible):
Show

Self-hosted native FCM (Android) push silently dropped - invalid `group` field in android.notification -> FCM HTTP 400

Summary

On a self-hosted instance, native-app push over FCM is silently dropped for every message. Devices registered with platform android_fcm never receive DM or channel notifications, even though the push subscription is valid and registered. Root cause: the gateway builds an FCM HTTP v1 message containing a field (group) that does not exist in FCM's AndroidNotification schema, so the FCM REST API rejects the entire request with HTTP 400. push_fcm swallows non-2xx responses with no log, so there is zero visibility - the gateway logs a complete, healthy-looking send path (evaluating eligibility -> fetching subscriptions -> sending to subscription -> worker completed) while every single message is 400'd.

Root cause

fluxer_gateway/src/push/push_fcm_payload.erl, build_android_notification/3:
build_android_notification(Tag, ImageUrl, Group) ->
    maybe_put(<<"image">>, ImageUrl, #{
        <<"channel_id">> => <<"fluxer_default_push">>,
        <<"tag">> => Tag,
        <<"group">> => Group,          %% <-- not a valid FCM v1 AndroidNotification field
        <<"click_action">> => <<"FLUXER_MESSAGE">>
    }).
FCM v1's AndroidNotification has no group field (Android notification grouping is a client-side concern via NotificationCompat.setGroup). FCM responds:
{ "error": { "code": 400, "status": "INVALID_ARGUMENT",
  "message": "Invalid JSON payload received. Unknown name \"group\" at 'message.android.notification': Cannot find field." } }

Why it's invisible

  • push_fcm_payload:handle_response/3 returns false silently on any non-2xx (no log line).
  • The self-hosted gateway image emits no logs at all: releases/*/sys.config sets the default logger handler to undefined, and docker_entrypoint.sh runs exec … foreground >/dev/null 2>&1 (see #665).
So the failure is completely silent end-to-end.

Reproduction

  1. Self-hosted (Docker Compose), Postgres backend, FCM configured (FLUXER_PUSH_FCM_*).
  2. Register a native Android FCM device; send it any message.
  3. Nothing arrives. A raw FCM v1 send to the same device token with the same payload shape reproduces it deterministically: with android.notification.group -> HTTP 400; remove it -> HTTP 200 and the notification is delivered.

Fix

Drop the group field. collapse_key (set in wrap_notification_message, a valid AndroidConfig field) still handles collapsing:
build_android_notification(Tag, ImageUrl) ->
    maybe_put(<<"image">>, ImageUrl, #{
        <<"channel_id">> => <<"fluxer_default_push">>,
        <<"tag">> => Tag,
        <<"click_action">> => <<"FLUXER_MESSAGE">>
    }).
Verified on a live self-hosted instance: native Android DM/channel push now delivers, tap-through works. iOS/APNs (ApnsPushService) and web-push/VAPID use different payload builders and are unaffected by this field.

Bonus - getting gateway logs (re #665)

Because the image discards all logs, you can attach a file logger to the running node at runtime via the release's RPC (no restart, fully reversible):
# with FLUXER_ERLANG_NODE_NAME / FLUXER_ERLANG_COOKIE / FLUXER_ERLANG_DIST_PORT
# set to the running node's values (entrypoint defaults: fluxer_gateway@127.0.0.1 /
# fluxer_gateway_dev_cookie / 8081):
bin/fluxer_gateway.real rpc logger add_handler \
  '[gwlog, logger_std_h, #{level => debug, config => #{file => "/tmp/gw.log"}}]'
bin/fluxer_gateway.real rpc logger set_primary_config '[level, debug]'
# ... read /tmp/gw.log ...
bin/fluxer_gateway.real rpc logger remove_handler '[gwlog]'
(Note: erl_call, the transport behind rpc, cannot parse binary literals - pass only integers/atoms/strings/simple maps.)

Environment

  • Self-hosted, Docker Compose, Postgres, ghcr.io/fluxerapp/fluxer-gateway.
  • Client: native flutter_client Android app (FCM). group present in upstream/main since the open-source re-architecture, so this affects all self-hosted FCM deployments.
(Diagnosis performed with Claude Code.)
Original by Rex
Show

Self-hosted native FCM (Android) push silently dropped - invalid `group` field in android.notification -> FCM HTTP 400

Summary

On a self-hosted instance, native-app push over FCM is silently dropped for every message. Devices registered with platform android_fcm never receive DM or channel notifications, even though the push subscription is valid and registered. Root cause: the gateway builds an FCM HTTP v1 message containing a field (group) that does not exist in FCM's AndroidNotification schema, so the FCM REST API rejects the entire request with HTTP 400. push_fcm swallows non-2xx responses with no log, so there is zero visibility - the gateway logs a complete, healthy-looking send path (evaluating eligibility -> fetching subscriptions -> sending to subscription -> worker completed) while every single message is 400'd.

Root cause

fluxer_gateway/src/push/push_fcm_payload.erl, build_android_notification/3:
build_android_notification(Tag, ImageUrl, Group) ->
    maybe_put(<<"image">>, ImageUrl, #{
        <<"channel_id">> => <<"fluxer_default_push">>,
        <<"tag">> => Tag,
        <<"group">> => Group,          %% <-- not a valid FCM v1 AndroidNotification field
        <<"click_action">> => <<"FLUXER_MESSAGE">>
    }).
FCM v1's AndroidNotification has no group field (Android notification grouping is a client-side concern via NotificationCompat.setGroup). FCM responds:
{ "error": { "code": 400, "status": "INVALID_ARGUMENT",
  "message": "Invalid JSON payload received. Unknown name \"group\" at 'message.android.notification': Cannot find field." } }

Why it's invisible

  • push_fcm_payload:handle_response/3 returns false silently on any non-2xx (no log line).
  • The self-hosted gateway image emits no logs at all: releases/*/sys.config sets the default logger handler to undefined, and docker_entrypoint.sh runs exec … foreground >/dev/null 2>&1 (see #1144).
So the failure is completely silent end-to-end.

Reproduction

  1. Self-hosted (Docker Compose), Postgres backend, FCM configured (FLUXER_PUSH_FCM_*).
  2. Register a native Android FCM device; send it any message.
  3. Nothing arrives. A raw FCM v1 send to the same device token with the same payload shape reproduces it deterministically: with android.notification.group -> HTTP 400; remove it -> HTTP 200 and the notification is delivered.

Fix

Drop the group field. collapse_key (set in wrap_notification_message, a valid AndroidConfig field) still handles collapsing:
build_android_notification(Tag, ImageUrl) ->
    maybe_put(<<"image">>, ImageUrl, #{
        <<"channel_id">> => <<"fluxer_default_push">>,
        <<"tag">> => Tag,
        <<"click_action">> => <<"FLUXER_MESSAGE">>
    }).
Verified on a live self-hosted instance: native Android DM/channel push now delivers, tap-through works. iOS/APNs (ApnsPushService) and web-push/VAPID use different payload builders and are unaffected by this field.

Bonus - getting gateway logs (re #1144)

Because the image discards all logs, you can attach a file logger to the running node at runtime via the release's RPC (no restart, fully reversible):
# with FLUXER_ERLANG_NODE_NAME / FLUXER_ERLANG_COOKIE / FLUXER_ERLANG_DIST_PORT
# set to the running node's values (entrypoint defaults: fluxer_gateway@127.0.0.1 /
# fluxer_gateway_dev_cookie / 8081):
bin/fluxer_gateway.real rpc logger add_handler \
  '[gwlog, logger_std_h, #{level => debug, config => #{file => "/tmp/gw.log"}}]'
bin/fluxer_gateway.real rpc logger set_primary_config '[level, debug]'
# ... read /tmp/gw.log ...
bin/fluxer_gateway.real rpc logger remove_handler '[gwlog]'
(Note: erl_call, the transport behind rpc, cannot parse binary literals - pass only integers/atoms/strings/simple maps.)

Environment

  • Self-hosted, Docker Compose, Postgres, ghcr.io/fluxerapp/fluxer-gateway.
  • Client: native flutter_client Android app (FCM). group present in upstream/main since the open-source re-architecture, so this affects all self-hosted FCM deployments.
(Diagnosis performed with Claude Code.)