Skip to content

Conversation

cabljac
Copy link

@cabljac cabljac commented Sep 30, 2025

This draft PR is a proposed solution to resolve #1712

Problem

When an analytics event payload contains malformed user properties, the SDK throws a TypeError before the user's callback can execute:

// Problematic payloads:
userProperties: {
  "some_property": null,                    // Entirely null
  "another_property": { "value": null },    // Value field is null
  "empty_property": {}                      // Missing value field
}

Error:

TypeError: Cannot convert undefined or null to object
at Function.keys ()
at unwrapValueAsString (analytics.js:206)

Solution

  1. Made unwrapValueAsString defensive
  • Added null/undefined/empty object checks before calling Object.keys()
  • Returns empty string for invalid inputs instead of throwing
  • Improved type safety by changing parameter from any to unknown
  1. Filter invalid user properties
  • Added filter in UserDimensions constructor to skip properties that are:
    • null or undefined
    • Empty objects {}
    • Objects with value field that is null, undefined, or {}
  • Valid properties (including falsy values like "" and 0) are preserved

@taeold
Copy link
Contributor

taeold commented Oct 6, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request provides a solid fix for a crash caused by malformed user properties in analytics events. The approach is robust, adding defensive checks in unwrapValueAsString and filtering invalid properties during event construction. The addition of a new test case that covers various invalid inputs is excellent for ensuring correctness and preventing future regressions. I have one suggestion to improve the readability of the new filtering logic.

@cabljac cabljac force-pushed the @invertase/app_remove-events branch from eee4ef2 to 16b1413 Compare October 13, 2025 12:00
@cabljac cabljac marked this pull request as ready for review October 13, 2025 12:02
@cabljac
Copy link
Author

cabljac commented Oct 15, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses a TypeError caused by malformed user properties in analytics events. The solution is well-implemented, particularly the defensive programming in unwrapValueAsString and the addition of comprehensive test cases to prevent regressions. I have one suggestion to further improve the robustness of the user property filtering logic to prevent partially-valid objects from being processed.

Comment on lines 184 to 198
.filter(([, v]) => {
// Property must be a non-empty object.
if (v == null || typeof v !== "object" || Object.keys(v).length === 0) {
return false;
}
// If 'value' field exists, it must not be null, undefined, or an empty object.
if (!("value" in v)) {
return true;
}

const value = (v as { value: unknown }).value;
const isEmptyObject =
typeof value === "object" && value !== null && Object.keys(value).length === 0;
return value != null && !isEmptyObject;
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current filtering logic for user properties is not strict enough. It allows property objects without a value field to be processed (e.g., an object containing only setTimestampUsec). This leads to the creation of UserPropertyValue instances where the value property is undefined, which violates its type definition (value: string).

To make the filtering more robust and ensure all processed user properties are valid, I suggest modifying the filter to explicitly require the presence of the value field. This also simplifies the logic, as the check for an empty object becomes redundant.

        .filter(([, v]) => {
          // Property must be an object and have a 'value' field.
          if (v == null || typeof v !== "object" || !("value" in v)) {
            return false;
          }

          // The 'value' field must not be null, undefined, or an empty object.
          const value = (v as { value: unknown }).value;
          const isEmptyObject =
            typeof value === "object" && value !== null && Object.keys(value).length === 0;
          return value != null && !isEmptyObject;
        })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Intermittent TypeError for app_remove events

2 participants