Skip to content

fix(rc): evaluate custom signals whose value is falsy, fix default types - #990

Open
Shubham-Padkonde wants to merge 2 commits into
firebase:mainfrom
Shubham-Padkonde:fix/remote-config-custom-signal-defaults
Open

Shubham-Padkonde wants to merge 2 commits into
firebase:mainfrom
Shubham-Padkonde:fix/remote-config-custom-signal-defaults

Conversation

@Shubham-Padkonde

@Shubham-Padkonde Shubham-Padkonde commented Sep 24, 2026 •

Copy link
Copy Markdown

A custom signal value of 0 (or 0.0 / False) in the evaluation context was treated as missing, because the value was read with context.get(key) or {} followed by if not value. As a result a condition such as NUMERIC_EQUAL ['0'] or NUMERIC_LESS_THAN ['1'] never matched for a signal of 0. Only a missing (None) signal is now skipped.

Also, as reported in #957, use '' and [] instead of {} as fallbacks for the operator, key and target values, and annotate _Value.as_int() as returning int.

Fixes #957

Hey there! So you want to contribute to a Firebase SDK?
Before you file this pull request, please read these guidelines:

Discussion

  • Read the contribution guidelines (CONTRIBUTING.md).
  • If this has been discussed in an issue, make sure to link to the issue here.
    If not, go file an issue about this before creating a pull request to discuss.

Testing

  • Make sure all existing tests in the repository pass after your change.
  • If you fixed a bug or added a feature, add a new test to cover your code.

API Changes

  • At this time we cannot accept changes that affect the public API. If you'd like to help
    us make Firebase APIs better, please propose your change in an issue so that we
    can discuss it together.

Context Sources

  • id: firebase-admin-python; path: AGENTS.md

Follow-up prepared with Codex assistance. Added 12 numeric-operator regression cases for empty list/dictionary values; all 47 Remote Config unit tests and pylint checks pass.

A custom signal value of 0 (or 0.0 / False) in the evaluation context
was treated as missing, because the value was read with
`context.get(key) or {}` followed by `if not value`. As a result a
condition such as NUMERIC_EQUAL ['0'] or NUMERIC_LESS_THAN ['1'] never
matched for a signal of 0. Only a missing (None) signal is now skipped.

Also, as reported in firebase#957, use '' and [] instead of {} as fallbacks for
the operator, key and target values, and annotate _Value.as_int() as
returning int.

Fixes firebase#957
@Shubham-Padkonde
Shubham-Padkonde requested a review from a team September 24, 2026 07:41

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

Copy link
Copy Markdown

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 modifies evaluate_custom_signal_condition in firebase_admin/remote_config.py to allow falsy custom signal values (such as 0) to be successfully evaluated, and updates the return type annotation of as_int to int. It also adds unit tests to verify zero and missing custom signal values. The review feedback highlights a potential bug where allowing non-numeric falsy values (like empty lists or dictionaries) could cause an unhandled TypeError in _compare_numbers, and suggests catching this exception as well as adding corresponding test cases.

Comment on lines +504 to 506
if actual_custom_signal_value is None:
logger.debug("Custom signal value not found in context: %s", custom_signal_key)
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

With the change to allow falsy values (such as empty lists [] or empty dictionaries {}) to pass through the None check, these values can now reach _compare_numbers when a numeric operator is evaluated.

In _compare_numbers, float(actual_value) is called. If actual_value is a list or dictionary, float() raises a TypeError (e.g., TypeError: float() argument must be a string or a real number, not 'list'). Since _compare_numbers only catches ValueError, this will cause an unhandled exception and crash the evaluation.

To prevent this, _compare_numbers should be updated to catch both ValueError and TypeError:

def _compare_numbers(self, custom_signal_key, target_value, actual_value, predicate_fn) -> bool:
    try:
        target = float(target_value)
        actual = float(actual_value)
        result = -1 if actual < target else 1 if actual > target else 0
        return predicate_fn(result)
    except (ValueError, TypeError):
        logger.warning('Invalid numeric value for comparison for custom signal key %s.', custom_signal_key)
        return False

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 348de76: numeric comparisons now reject both ValueError and TypeError. Added empty-list and empty-dictionary cases for all six numeric operators: all 12 failed before the fix and now pass; the full Remote Config module passes all 47 tests. Source/test pylint and git diff --check pass. Prepared with Codex assistance.

Comment on lines +843 to +847
def test_evaluate_custom_signal_missing_value(self):
server_template = self._custom_signal_template(
CustomSignalOperator.NUMERIC_LESS_THAN.value, ['1'])
server_config = server_template.evaluate({'randomization_id': '123'})
assert server_config.get_boolean('is_enabled') is False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

It would be highly beneficial to add test cases where the custom signal value in the context is an empty list [] or an empty dictionary {}. This will ensure that these falsy, non-numeric types are handled gracefully without raising a TypeError during numeric comparisons.

This branch has not been deployed

No deployments
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.

Fix typos and wrong types in remote_config.py

1 participant