Skip to content

fix(memprovider): use int64 type in IntEvaluation to match API#464

Merged
erka merged 9 commits intoopen-feature:mainfrom
leoromanovsky:fix/memprovider-int64-type-mismatch
Jan 23, 2026
Merged

fix(memprovider): use int64 type in IntEvaluation to match API#464
erka merged 9 commits intoopen-feature:mainfrom
leoromanovsky:fix/memprovider-int64-type-mismatch

Conversation

@leoromanovsky
Copy link
Contributor

@leoromanovsky leoromanovsky commented Jan 23, 2026

Motivation

When using the InMemoryProvider for testing with integer flags, users encounter unexpected TYPE_MISMATCH errors even when storing values as int64:

  provider := memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{
      "max-retries": {
          State:          memprovider.Enabled,
          DefaultVariant: "high",
          Variants:       map[string]any{"high": int64(10)},
      },
  })

  result := provider.IntEvaluation(ctx, "max-retries", 1, nil)
  // Expected: result.Value == 10
  // Actual: result.Value == 1, result.ResolutionError == TYPE_MISMATCH

This makes the InMemoryProvider unusable for integer flag testing.

Root Cause

The IntEvaluation method calls genericResolve[int] (line 98), but the FeatureProvider interface specifies int64:

  // provider.go:55 - The official interface
  IntEvaluation(ctx context.Context, flag string, defaultValue int64, flatCtx FlattenedContext) IntResolutionDetail

The genericResolve helper performs a type assertion value.(T). When T is int but the stored value is int64, the assertion fails because int64 != int in Go's type system, even on 64-bit platforms.

Compare with FloatEvaluation which correctly uses genericResolve[float64].

Changes

  1. in_memory_provider.go: Changed genericResolve[int] to genericResolve[int64] to match the API contract
  2. in_memory_provider_test.go: Updated test to use explicit int64() types
  3. testprovider_test.go: Updated test to use explicit int64() types

Design Decision

This fix requires users to explicitly type integer variants as int64:

  // Correct usage after this fix
  Variants: map[string]any{"high": int64(10)}

  // Will not work (untyped constant becomes int)
  Variants: map[string]any{"high": 10}

This is consistent with:

  • The FeatureProvider interface contract which specifies int64
  • The FloatEvaluation behavior which requires float64
  • The documented return type IntEvaluationDetails = GenericEvaluationDetails[int64]

An alternative would be to handle both int and int64 in genericResolve, but this would add complexity and deviate from the pattern used by other evaluation methods.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @leoromanovsky, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical type mismatch bug within the InMemoryProvider's IntEvaluation method. Previously, the method incorrectly assumed int values, leading to TYPE_MISMATCH errors when users stored int64 values, as mandated by the OpenFeature API. The fix updates the internal type resolution to int64, ensuring seamless and correct evaluation of integer feature flags, thereby improving the robustness and compliance of the provider.

Highlights

  • Type Mismatch Resolution: The IntEvaluation method in InMemoryProvider now correctly uses int64 for type resolution, addressing TYPE_MISMATCH errors that occurred when int64 values were stored in Variants.
  • API Alignment: The change aligns the IntEvaluation implementation with the OpenFeature API's specification, which defines IntValue/IntEvaluation as returning int64.
  • Test Case Enhancement: Test cases for IntEvaluation have been updated to explicitly use int64 types in Variants and to ensure correct error message formatting, reflecting the expected usage pattern.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

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 correctly addresses a type mismatch issue in IntEvaluation by ensuring int64 is used consistently, which aligns with the OpenFeature API specification. The changes are logical and the updated tests properly verify the fix. I've included a couple of suggestions to enhance code readability by replacing magic numbers with constants from the math package.

The IntEvaluation method was using genericResolve[int] which caused
TYPE_MISMATCH errors when users stored int64 values in Variants.

The FeatureProvider interface defines IntEvaluation with int64:
  IntEvaluation(ctx, flag string, defaultValue int64, ...) IntResolutionDetail

The type assertion in genericResolve should use int64 to match.

Before this fix:
- User stores: Variants{"max": int64(100)}
- genericResolve[int] tries: value.(int)
- Type assertion fails because int64 != int
- Returns default value with TYPE_MISMATCH error

After this fix:
- User stores: Variants{"max": int64(100)}
- genericResolve[int64] tries: value.(int64)
- Type assertion succeeds
- Returns correct value

Updated tests to use explicit int64 types in Variants to match the
expected usage pattern for the IntEvaluation API.

Signed-off-by: Leo Romanovsky <leo.romanovsky@datadoghq.com>
@leoromanovsky leoromanovsky force-pushed the fix/memprovider-int64-type-mismatch branch from 793a5c1 to 4ec983d Compare January 23, 2026 02:58
@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.23%. Comparing base (1972034) to head (5b4c782).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #464      +/-   ##
==========================================
+ Coverage   83.11%   83.23%   +0.11%     
==========================================
  Files          27       27              
  Lines        2097     2111      +14     
==========================================
+ Hits         1743     1757      +14     
  Misses        305      305              
  Partials       49       49              
Flag Coverage Δ
e2e 83.23% <100.00%> (+0.11%) ⬆️
unit 83.23% <100.00%> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Signed-off-by: Leo Romanovsky <leo.romanovsky@datadoghq.com>
@leoromanovsky leoromanovsky marked this pull request as ready for review January 23, 2026 03:03
@leoromanovsky leoromanovsky requested review from a team as code owners January 23, 2026 03:03
@sahidvelji sahidvelji mentioned this pull request Jan 23, 2026
Copy link
Member

@erka erka left a comment

Choose a reason for hiding this comment

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

these changes doesn't resolve the issue, now int has the same problem.

@erka
Copy link
Member

erka commented Jan 23, 2026

I would suggest to modify genericResolve func to this

// genericResolve is a helper to extract type verified evaluation and fill openfeature.ProviderResolutionDetail
func genericResolve[T comparable](value any, defaultValue T, detail *openfeature.ProviderResolutionDetail) T {
	switch v := value.(type) {
	case int8:
		value = int64(v)
	case int16:
		value = int64(v)
	case int32:
		value = int64(v)
	case int:
		value = int64(v)
	case float32:
		value = float64(v)
	}

	v, ok := value.(T)

	if ok {
		return v
	}

	detail.Reason = openfeature.ErrorReason
	detail.ResolutionError = openfeature.NewTypeMismatchResolutionError("incorrect type association")
	return defaultValue
}

We need to have tests for int and int64.

Please revert the changes to the testing package to ensure there are no breaking changes for users.

Address review feedback by making genericResolve more forgiving.
Instead of requiring explicit int64 values, the function now coerces
smaller integer types (int, int8, int16, int32) to int64 and float32
to float64.

This allows users to write natural Go code without explicit casts:

    Variants: map[string]any{"value": 42}  // works now

Instead of requiring:

    Variants: map[string]any{"value": int64(42)}

Added test coverage for both int and int64 variants.

Signed-off-by: Leo Romanovsky <leo.romanovsky@datadoghq.com>
Add test coverage for all coerced integer types (int8, int16, int32, int)
in addition to int64.

Signed-off-by: Leo Romanovsky <leo.romanovsky@datadoghq.com>
Revert the int64 cast in testprovider_test.go since genericResolve
now coerces int to int64 automatically. This avoids breaking changes
for existing users.

Signed-off-by: Leo Romanovsky <leo.romanovsky@datadoghq.com>
@leoromanovsky
Copy link
Contributor Author

I would suggest to modify genericResolve func to this

// genericResolve is a helper to extract type verified evaluation and fill openfeature.ProviderResolutionDetail
func genericResolve[T comparable](value any, defaultValue T, detail *openfeature.ProviderResolutionDetail) T {
	switch v := value.(type) {
	case int8:
		value = int64(v)
	case int16:
		value = int64(v)
	case int32:
		value = int64(v)
	case int:
		value = int64(v)
	case float32:
		value = float64(v)
	}

	v, ok := value.(T)

	if ok {
		return v
	}

	detail.Reason = openfeature.ErrorReason
	detail.ResolutionError = openfeature.NewTypeMismatchResolutionError("incorrect type association")
	return defaultValue
}

We need to have tests for int and int64.

Please revert the changes to the testing package to ensure there are no breaking changes for users.

Thanks for the thoughtful feedback; I understand your perspective.

@leoromanovsky leoromanovsky requested a review from erka January 23, 2026 14:14
…riants

Signed-off-by: Roman Dmytrenko <rdmytrenko@gmail.com>
@erka
Copy link
Member

erka commented Jan 23, 2026

@leoromanovsky thank you

fix(memprovider): add type conversion for int8/int16/int32/float32 va…
Signed-off-by: Roman Dmytrenko <rdmytrenko@gmail.com>
@erka
Copy link
Member

erka commented Jan 23, 2026

@sahidvelji are you still okay with this PR?

@sahidvelji
Copy link
Contributor

Do we need to account for unsigned integers also? It won't be possible to coerce a uint64 to int64 safely. Maybe we could document that only signed ints are supported? What do you think?
https://pkg.go.dev/builtin#uint

@erka
Copy link
Member

erka commented Jan 23, 2026

To be honest, I don’t have a solid answer. I agree that adding that only signed ints are supported to the docs is the best option right now. @sahidvelji

Signed-off-by: Roman Dmytrenko <rdmytrenko@gmail.com>
@erka erka merged commit 6085165 into open-feature:main Jan 23, 2026
6 checks passed
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.

3 participants