Summary
When Mixpanel.addGroup(groupKey, groupID) is called more than once with the same (groupKey, groupID) — e.g. across repeated identify calls, React effects, or simply across app launches (the SDK persists super properties) — Android ends up with duplicated entries in the groupKey super-property array. That array then rides along on every emitted event.
iOS dedupes correctly. Android does not. This is a long-standing inconsistency between the two natives that bites every cross-platform consumer.
Reproduction
const mixpanel = new Mixpanel(token, true);
await mixpanel.init();
mixpanel.identify("user-1");
mixpanel.addGroup("workspace_id", "A");
mixpanel.addGroup("workspace_id", "A");
mixpanel.addGroup("workspace_id", "A");
mixpanel.track("Page View");
Expected
workspace_id super property on the emitted event = ["A"].
Actual
- Android:
workspace_id = ["A", "A", "A"]
- iOS:
workspace_id = ["A"]
Because super properties are persisted across launches, on Android the array also grows session over session — even a single addGroup per launch accumulates [A, A, A, A, …] indefinitely.
Root cause
The RN JS wrapper passes through to the native addGroup (https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L391-L395):
addGroup(groupKey, groupID) {
// ...
this.mixpanelImpl.addGroup(this.token, groupKey, groupID);
}
Android (broken)
com.mixpanel.android.mpmetrics.MixpanelAPI.addGroup calls updateSuperProperties(new SuperPropertyUpdate() {...}) whose update(JSONObject in) body is just in.accumulate(groupKey, groupID). JSONObject.accumulate appends to the array unconditionally — no membership check.
https://github.com/mixpanel/mixpanel-android/blob/master/analytics/src/main/java/com/mixpanel/android/mpmetrics/MixpanelAPI.java#L1286-L1304
iOS (correct)
MixpanelInstance.swift dedupes:
if let oldValue = oldValue as? [MixpanelType] {
var vals = oldValue
if !vals.contains(where: { $0.equals(rhs: groupID) }) {
vals.append(groupID)
superProperties[groupKey] = vals
}
}
Versions affected
mixpanel-react-native: v3.1.2 and v3.3.0 (verified)
mixpanel-android: 8.2.0 through master post-v8.7.0 (verified by reading source)
Bumping either package does not fix this.
Suggested fix
Two options, in order of preference:
1. Fix upstream in mixpanel-android
Change MixpanelAPI.addGroup to check existing membership before appending, matching the iOS implementation. This makes the native SDKs consistent and removes the foot-gun for any consumer (not just RN).
If that's the case I can move the issue there.
2. Dedupe at the React Native shim layer
Mirrors what javascript/mixpanel-main.js:525-535 already does for JS-mode fallback:
// index.js
async addGroup(groupKey, groupID) {
if (!StringHelper.isValid(groupKey)) {
StringHelper.raiseError(PARAMS.GROUP_KEY);
}
// Read current super property, dedupe before forwarding
const currentValue = await this.mixpanelImpl.getSuperProperty(this.token, groupKey);
if (Array.isArray(currentValue) && currentValue.includes(groupID)) {
// Still update the People profile membership server-side
await this.mixpanelImpl.union?.(this.token, { [groupKey]: [groupID] });
return;
}
this.mixpanelImpl.addGroup(this.token, groupKey, groupID);
}
Option 1 is the cleanest. Option 2 is faster to ship and benefits only RN consumers — but RN is where the cross-platform discrepancy hurts most.
People profile is unaffected
The People-side union call within addGroup is set-valued server-side, so the People profile's groupKey array stays clean. The bug is only visible in raw event payloads — every event carries the bloated workspace_id super property. This is why it's easy to miss when looking at the Mixpanel UI.
Current workaround
Call setGroup(groupKey, groupID) for the first entry of each identify pass (which overwrites the super property via registerSuperProperties), then addGroup for the rest. Each identify then starts from a fresh array:
if (workspaces.length > 0) {
mixpanel.setGroup("workspace_id", workspaces[0].id);
for (let i = 1; i < workspaces.length; i++) {
mixpanel.addGroup("workspace_id", workspaces[i].id);
}
}
Summary
When
Mixpanel.addGroup(groupKey, groupID)is called more than once with the same(groupKey, groupID)— e.g. across repeatedidentifycalls, React effects, or simply across app launches (the SDK persists super properties) — Android ends up with duplicated entries in thegroupKeysuper-property array. That array then rides along on every emitted event.iOS dedupes correctly. Android does not. This is a long-standing inconsistency between the two natives that bites every cross-platform consumer.
Reproduction
Expected
workspace_idsuper property on the emitted event =["A"].Actual
workspace_id = ["A", "A", "A"]workspace_id = ["A"]Because super properties are persisted across launches, on Android the array also grows session over session — even a single
addGroupper launch accumulates[A, A, A, A, …]indefinitely.Root cause
The RN JS wrapper passes through to the native
addGroup(https://github.com/mixpanel/mixpanel-react-native/blob/v3.3.0/index.js#L391-L395):Android (broken)
com.mixpanel.android.mpmetrics.MixpanelAPI.addGroupcallsupdateSuperProperties(new SuperPropertyUpdate() {...})whoseupdate(JSONObject in)body is justin.accumulate(groupKey, groupID).JSONObject.accumulateappends to the array unconditionally — no membership check.https://github.com/mixpanel/mixpanel-android/blob/master/analytics/src/main/java/com/mixpanel/android/mpmetrics/MixpanelAPI.java#L1286-L1304
iOS (correct)
MixpanelInstance.swiftdedupes:Versions affected
mixpanel-react-native: v3.1.2 and v3.3.0 (verified)mixpanel-android: 8.2.0 throughmasterpost-v8.7.0 (verified by reading source)Bumping either package does not fix this.
Suggested fix
Two options, in order of preference:
1. Fix upstream in
mixpanel-androidChange
MixpanelAPI.addGroupto check existing membership before appending, matching the iOS implementation. This makes the native SDKs consistent and removes the foot-gun for any consumer (not just RN).If that's the case I can move the issue there.
2. Dedupe at the React Native shim layer
Mirrors what
javascript/mixpanel-main.js:525-535already does for JS-mode fallback:Option 1 is the cleanest. Option 2 is faster to ship and benefits only RN consumers — but RN is where the cross-platform discrepancy hurts most.
People profile is unaffected
The People-side
unioncall withinaddGroupis set-valued server-side, so the People profile'sgroupKeyarray stays clean. The bug is only visible in raw event payloads — every event carries the bloatedworkspace_idsuper property. This is why it's easy to miss when looking at the Mixpanel UI.Current workaround
Call
setGroup(groupKey, groupID)for the first entry of each identify pass (which overwrites the super property viaregisterSuperProperties), thenaddGroupfor the rest. Each identify then starts from a fresh array: