Repository navigation
Description
Summary
A boolean flag satisfies an Integer request. Evaluating a boolean flag through get_integer_details returns True with reason STATIC and no error code, where the specification requires the code default and TYPE_MISMATCH.
The application sees a plausible value — True behaves as 1 in arithmetic — and no indication that anything went wrong. That is the worst failure mode a feature flag has.
Cause
_typecheck_flag_value in openfeature/client.py:
type_map: TypeMap = {
FlagType.BOOLEAN: bool,
FlagType.STRING: str,
FlagType.OBJECT: (dict, list),
FlagType.FLOAT: float,
FlagType.INTEGER: int,
}
py_type = type_map.get(flag_type)
...
if not isinstance(value, py_type):
return TypeMismatchError(f"Expected type {py_type} but got {type(value)}")bool is a subclass of int in Python, so isinstance(True, int) is True and the check passes:
>>> isinstance(True, int)
TrueThe reverse direction is fine — isinstance(1, float) is False, so an integer requested as a float is correctly a mismatch — which is why this is easy to miss.
Reproducing
from openfeature import api
from openfeature.provider.in_memory_provider import InMemoryFlag, InMemoryProvider
api.set_provider(InMemoryProvider({
"boolean-flag": InMemoryFlag(default_variant="on", variants={"on": True, "off": False}),
}))
details = api.get_client().get_integer_details("boolean-flag", 1)
print(details.value, details.reason, details.error_code)
# True Reason.STATIC None
# expected: 1 Reason.ERROR ErrorCode.TYPE_MISMATCHSuggested fix
Reject bool explicitly when an integer or float was requested:
if flag_type in (FlagType.INTEGER, FlagType.FLOAT) and isinstance(value, bool):
return TypeMismatchError(f"Expected type {py_type} but got {type(value)}")Worth checking the same place for FlagType.OBJECT: (dict, list) accepts a list, which is correct per the spec's structure type, but is worth a test either way.
How it surfaced
Building the Python implementation of the cross-language provider conformance suite (spec#417). The suite runs an identical type-mismatch matrix against every provider in every language; this row passes everywhere else and fails only in Python, because only Python has bool as a subclass of int.
That is a reasonable advertisement for having more than one language implementation: a Java or Go suite could never have caught this.
The suite currently marks the case xfail(strict=True) with a pointer to this issue, so it stays visible and un-hides itself automatically once this is fixed.
Activity
feat: enforce reasons, and let a single scenario carry a known deviation
fix: reject boolean values for integer flags
Hexecu commentedon Aug 30, 2026
Thanks for the clear reproduction. I confirmed the behavior with
InMemoryProviderand opened #621.The fix rejects booleans for integer requests while preserving other integer subclasses. I also found that the async type-mismatch path reported the error but still returned the rejected value, so the PR makes it return the caller’s default as well.
I checked the in-memory conformance test from open-feature/python-sdk-contrib#409 too: the case marked for this issue now passes with the patch. The PR includes a runnable before/after example and regression tests for both sync and async getters.
fix: reject boolean values for integer flags (#621)
feat: enforce reasons, and let a single scenario carry a known deviation
feat: enforce reasons, and let a single scenario carry a known deviation
feat: enforce reasons, and let a single scenario carry a known deviation
feat: enforce reasons, and let a single scenario carry a known deviation
feat: enforce reasons, and let a single scenario carry a known deviation
test(ofrep): run the provider conformance suite against flagd's OFREP…
feat: enforce reasons, and let a single scenario carry a known deviation
feat: enforce reasons, and let a single scenario carry a known deviation
test(ofrep): run the provider conformance suite against flagd's OFREP…
feat: enforce reasons, and let a single scenario carry a known deviation
54 remaining items
feat(provider-tck): add the Python conformance suite for OpenFeature …
test(ofrep): run the provider conformance suite against flagd's OFREP…
feat: enforce reasons, and let a single scenario carry a known deviation
test(ofrep): run the provider conformance suite against flagd's OFREP…
feat(provider-tck): add the Python conformance suite for OpenFeature …
test(ofrep): run the provider conformance suite against flagd's OFREP…
test(ofrep): run the provider conformance suite against flagd's OFREP…
test(ofrep): run the provider conformance suite against flagd's OFREP…
test(ofrep): run the provider conformance suite against flagd's OFREP…
feat(provider-tck): add the Python conformance suite for OpenFeature …
test(ofrep): run the provider conformance suite against flagd's OFREP…
test(ofrep): run the provider conformance suite against flagd's OFREP…
feat(provider-tck): add the Python conformance suite for OpenFeature …
test(ofrep): run the provider conformance suite against flagd's OFREP…
test(ofrep): run the provider conformance suite against flagd's OFREP…