Short answer: - Undefined means the property wasn’t set at all (missing), so Mixpanel has no value for that property on the event or user profile at query time. It appears as “(not set)” in the UI. [Falsy values] - 0 is an actual numeric value that was explicitly set to zero. It’s a valid number, not the same as undefined. [Data types]Why you might see “Undefined” for a profile property - The user profile doesn’t have that property yet, or the user has no profile at all when you segment by a user property. [Falsy values; Profiles FAQ] - For geolocation defaults (city/region/country), Undefined can occur if IP wasn’t mapped or not included (common for server-side calls if you suppress or omit IP). [Falsy values; Node geolocation note; Java geolocation note]Why you might see “0” - You (or an SDK/job) explicitly set a numeric property to 0, or incremented a property down to 0. For example with $add/increment operators. [Operators; JS increment; Node increment; Python increment]Key behavioral differences - Undefined is absence; it results when a property isn’t present. It’s grouped as (not set) in reports and can be filtered out. [Falsy values] - 0 is present, numeric, and will participate in numeric operations (sum, median, percentiles, increments). [Data types]Could this be a break in people.set()? Playing devil’s advocate on root causes - Profile not identified/associated: In JS, if you set people properties before identify(), updates are queued and may not be applied to the intended user yet—leading you to think a property is “missing.” [JS storing profiles]
- Writing to the wrong distinct_id: If your events and profiles use different distinct_id values, the join at query time won’t find the profile, so the property appears undefined in event reports. [Data model; Profiles FAQ join behavior] - Overwrites vs set-once: Using people.set() overwrites values; people.set_once() skips overwriting. If you expect a value but previously wrote with set_once when the property already existed, it won’t update—may remain 0 or old value. [Python set/set_once; [Other types Python]; JS set/set_once; [Other types JS]; Node set/set_once; [Other types Node]] - Incrementing down to zero: Using $add/people.increment with a negative value can legitimately produce 0; that’s expected, not a break. [Operators; [Other types JS]; [Other types Node]; [Other types Python]] - Geolocation defaults: If you suppress IP (e.g., ip: '0' or $ip: 0) when updating profiles server-side, location fields will stay undefined—by design. [Node storing profiles; Java storing profiles] - Late profile creation: Since joins happen at query time using the latest state, if you create/update profiles after events, they will retroactively join—but until the profile exists, earlier queries could show undefined. [Profiles FAQ join behavior]How to diagnose - Verify the distinct_id used in events equals the profile’s $distinct_id. - Check your write path: are you calling identify() before people.set() in JS? [JS storing profiles]
- Inspect recent writes: Did you use set_once instead of set, or $add/people.increment that led to 0? [Operators] - Confirm whether you’re suppressing IP or missing it for geo fields. [Falsy values] - In reports, exclude (not set) to see only defined values if that’s your intent. [Falsy values]If you share an example (property name, SDK used, and a snippet of your set/increment call), I can help pinpoint which of the above is happening.
kapa.ai is still learning and improving, please let me know how I did by reacting below.
ℹ️ Tip: Mention @kapa.ai in the thread for follow-ups or DM the app for 1-to-1 help.
