User data field validation - infobip/mobile-messaging-sdk-android GitHub Wiki

User Data Field Validation

Overview

Starting from version 14.7.2, the Mobile Messaging SDK enforces validation of all user data fields against the Customer Profiles API (formerly People API) limits before sending data to the server. This ensures data integrity and provides immediate feedback when field constraints are violated.

Field Limits

The following limits are enforced based on the Customer Profiles API (formerly People API) specification:

Field Limit Description
firstName 255 characters Person's first name
lastName 255 characters Person's last name
middleName 50 characters Person's middle name
externalUserId 256 characters Unique ID for a person from your external system
emails 100 emails Maximum number of email addresses per person
phones 100 phone numbers Maximum number of phone numbers per person
customAttributes (per value) 4096 characters Maximum length per custom attribute value

Validation Behavior

Synchronous Operations (with ResultListener)

When using synchronous operations with a ResultListener, validation errors are returned immediately in the error callback:

User user = new User();
user.setFirstName(veryLongString); // > 255 characters

mobileMessaging.saveUser(user, new MobileMessaging.ResultListener<User>() {
    @Override
    public void onResult(Result<User> result) {
        if (result.isSuccess()) {
            // User saved successfully
            User savedUser = result.getData();
        } else {
            // Validation error or other error
            MobileMessagingError error = result.getError();
            Log.e("TAG", "Error: " + error.getMessage());
        }
    }
});

Error handling:

  • If validation fails, the ResultListener will receive a Result with an error
  • The error message will detail which fields violated the constraints
  • No partial updates are made - if any field is invalid, the entire operation is rejected
  • The error message includes a link to the API documentation for reference

Asynchronous Operations (without ResultListener)

When using asynchronous operations without a ResultListener, validation exceptions are not thrown to the caller:

User user = new User();
user.setFirstName(veryLongString); // > 255 characters

// This call will fail silently - no exception is thrown
mobileMessaging.saveUser(user);

Important notes:

  • Validation errors are logged but not thrown as exceptions
  • The operation will fail silently from the caller's perspective
  • If you need error handling, always use the synchronous variant with a ResultListener

Example: Handling Validation Errors

Valid User Data

User user = new User();
user.setFirstName("John");
user.setLastName("Doe");
user.setMiddleName("M");
user.setExternalUserId("user123");
user.setEmails(Collections.singleton("[email protected]"));
user.setPhones(Collections.singleton("+1234567890"));

Map<String, CustomAttributeValue> customAttributes = new HashMap<>();
customAttributes.put("subscriptionLevel", new CustomAttributeValue("premium"));
user.setCustomAttributes(customAttributes);

// This will succeed
mobileMessaging.saveUser(user, new MobileMessaging.ResultListener<User>() {
    @Override
    public void onResult(Result<User> result) {
        if (result.isSuccess()) {
            Log.d("TAG", "User saved successfully");
        }
    }
});

Invalid User Data (Field Too Long)

User user = new User();
// This string is 260 characters - exceeds the 255 limit
String veryLongFirstName = "a".repeat(260);
user.setFirstName(veryLongFirstName);

mobileMessaging.saveUser(user, new MobileMessaging.ResultListener<User>() {
    @Override
    public void onResult(Result<User> result) {
        if (!result.isSuccess()) {
            // Validation error will be caught here
            MobileMessagingError error = result.getError();
            Log.e("TAG", "Validation failed: " + error.getMessage());
            // Error message will contain:
            // "User data validation failed:
            //   - firstName exceeds maximum length of 255 characters (actual: 260)
            // Please check the field limits at: https://www.infobip.com/docs/api/customer-engagement/people/update-a-person"
        }
    }
});

Invalid User Data (Too Many Emails)

User user = new User();
Set<String> emails = new HashSet<>();
for (int i = 0; i < 105; i++) {
    emails.add("email" + i + "@infobip.com");
}
user.setEmails(emails); // Exceeds 100 email limit

mobileMessaging.saveUser(user, new MobileMessaging.ResultListener<User>() {
    @Override
    public void onResult(Result<User> result) {
        if (!result.isSuccess()) {
            Log.e("TAG", "Error: " + result.getError().getMessage());
            // Error message will contain:
            // "User data validation failed:
            //   - emails count exceeds maximum of 100 (actual: 105)
            // Please check the field limits at: https://www.infobip.com/docs/api/customer-engagement/people/update-a-person"
        }
    }
});

Multiple Validation Errors

User user = new User();
user.setFirstName("a".repeat(260)); // Too long
user.setMiddleName("b".repeat(55)); // Too long
user.setExternalUserId("c".repeat(300)); // Too long

mobileMessaging.saveUser(user, new MobileMessaging.ResultListener<User>() {
    @Override
    public void onResult(Result<User> result) {
        if (!result.isSuccess()) {
            // All validation errors will be reported together
            Log.e("TAG", result.getError().getMessage());
            // Error message will contain all three violations:
            // "User data validation failed:
            //   - firstName exceeds maximum length of 255 characters (actual: 260)
            //   - middleName exceeds maximum length of 50 characters (actual: 55)
            //   - externalUserId exceeds maximum length of 256 characters (actual: 300)
            // Please check the field limits at: https://www.infobip.com/docs/api/customer-engagement/people/update-a-person"
        }
    }
});

Affected Methods

Field validation is applied to the following SDK methods:

User Data Methods

  • MobileMessaging.saveUser(User user, ResultListener<User> listener)
  • MobileMessaging.saveUser(User user) (async - errors not reported to caller)

Personalization Methods

  • MobileMessaging.personalize(UserIdentity userIdentity, UserAttributes userAttributes, ResultListener<User> listener)
  • MobileMessaging.personalize(UserIdentity userIdentity, UserAttributes userAttributes, boolean forceDepersonalize, ResultListener<User> listener)

Best Practices

  1. Always use ResultListener for production code - This ensures you can handle validation errors gracefully
  2. Validate data on the client side - Consider adding input validation in your UI to prevent users from entering data that exceeds limits
  3. Handle errors gracefully - Show user-friendly error messages when validation fails
  4. Test edge cases - Test with data at the boundary limits (e.g., exactly 255 characters) to ensure your app handles limits correctly
  5. Log validation errors - Always log validation errors for debugging purposes

Migration Guide

If you're upgrading from a previous version:

Before (< 14.7.2)

// Invalid data would be sent to the server
// Server would reject it and return an error
User user = new User();
user.setFirstName(veryLongString);
mobileMessaging.saveUser(user, listener);
// Error would come back from the server after network round-trip

After (>= 14.7.2)

// Invalid data is caught immediately before sending to server
User user = new User();
user.setFirstName(veryLongString);
mobileMessaging.saveUser(user, listener);
// Error is returned immediately in the listener, no network request made

Custom Attribute Validation

Custom attributes have a per-value limit of 4096 characters. The validation checks the string representation of the value:

Map<String, CustomAttributeValue> customAttributes = new HashMap<>();

// Valid - string value
customAttributes.put("description", new CustomAttributeValue("Short description"));

// Valid - number value (converted to string for validation)
customAttributes.put("age", new CustomAttributeValue(25));

// Valid - boolean value (converted to string for validation)
customAttributes.put("isPremium", new CustomAttributeValue(true));

// Invalid - string too long
customAttributes.put("longText", new CustomAttributeValue("a".repeat(5000))); // Will fail validation

user.setCustomAttributes(customAttributes);

Logging

Validation errors are logged at ERROR level:

E/MobileMessaging: USER DATA VALIDATION ERROR
    org.infobip.mobile.messaging.mobileapi.user.UserDataValidationException: User data validation failed:
      - firstName exceeds maximum length of 255 characters (actual: 260)

    Please check the field limits at: https://www.infobip.com/docs/api/customer-engagement/people/update-a-person

Testing

When writing tests, you can catch validation exceptions:

@Test
public void testInvalidFirstName() {
    User user = new User();
    user.setFirstName("a".repeat(260));

    mobileMessaging.saveUser(user, new MobileMessaging.ResultListener<User>() {
        @Override
        public void onResult(Result<User> result) {
            assertFalse(result.isSuccess());
            assertNotNull(result.getError());
            assertTrue(result.getError().getMessage().contains("firstName"));
        }
    });
}

Troubleshooting

Q: My saveUser call is failing but I don't see any error A: Make sure you're using the synchronous variant with a ResultListener. The async variant (without listener) will not report errors to the caller.

Q: Can I disable validation? A: No, validation cannot be disabled as it enforces API constraints that would otherwise be rejected by the server.

Q: What happens if I call saveUser without a listener? A: The validation will still occur, but errors will only be logged and not reported to your code. Use a ResultListener if you need error handling.

Q: Are partial updates allowed if some fields are valid? A: No, validation follows an all-or-nothing approach. If any field is invalid, the entire operation is rejected with no partial updates.

Q: How can I check if my data will pass validation before calling saveUser? A: You can use the UserDataValidator.validate() methods directly (though this is an internal API):

try {
    UserDataValidator.validate(user);
    // Data is valid
} catch (UserDataValidationException e) {
    // Data is invalid
    Log.e("TAG", e.getMessage());
}

Additional Resources

⚠️ **GitHub.com Fallback** ⚠️