Skip to content

fix: do not log an error per evaluation for an absent string comparison property - #2019

Draft
scottt732 wants to merge 1 commit into
open-feature:mainfrom
scottt732:fix/string-comparison-absent-property
Draft

fix: do not log an error per evaluation for an absent string comparison property#2019
scottt732 wants to merge 1 commit into
open-feature:mainfrom
scottt732:fix/string-comparison-absent-property

Conversation

@scottt732

Copy link
Copy Markdown

Fixes #2018. Opening as a draft since #2018 proposes a direction and I'd rather confirm it before you spend review time — happy to change the severities, the sentinel, or the shared-helper refactor.

The problem

A starts_with / ends_with rule referencing a context attribute the client did not send logs at error level, with a stack trace, on every evaluation. jsonLogic resolves a missing var to nil, and parseStringComparisonEvaluationData could not tell that apart from a wrong-typed operand, so both took the same error path.

Two properties of that path make it expensive:

  • it runs per evaluation, so volume tracks request rate rather than the number of bad rules
  • zap.Config.Build() attaches a stack trace at error level, turning each occurrence into ~40 log lines

A rule that ORs several version prefixes multiplies again — one of ours had eight starts_with calls, so a single bulk OFREP evaluation produced several hundred lines. That exhausted our organisation's daily log-index quota in about nine minutes and stopped log indexing for every service we run until the quota reset. The flag resolved correctly throughout; the entire cost was log volume.

The change

parseStringComparisonEvaluationData returns a distinct sentinel when the property operand is nil, which lets the two cases be reported differently:

Case Before After
property absent from context error + stack trace, per evaluation debug
property present, wrong type error + stack trace, per evaluation warn, no stack trace

Rationale: referencing an optional attribute is ordinary and the rule simply does not match, so it isn't an error condition at all. A wrong-typed operand is a real misconfiguration and stays visible — but at a level that doesn't attach a stack trace, since the volume is still driven by request rate.

Both continue to return nil to jsonLogic, so evaluation results are unchanged. The two evaluators now share the comparison and logging path, keeping that policy in one place.

Testing

New cases cover the absent/wrong-type distinction, that a wrong-typed operand is not treated as absent, that neither errors the evaluation, and that ordinary string comparison still works. Full core and flagd suites pass.

Verified against the reproduction in #2018 — evaluation output byte-identical, log output transformed:

A. absent key:  {"value":false,"reason":"TARGETING_MATCH","variant":"off"}
B. matching:    {"value":true, "reason":"TARGETING_MATCH","variant":"on"}
C. non-string:  {"value":false,"reason":"TARGETING_MATCH","variant":"off"}

error lines:       0     (was 1 per evaluation)
stack frame lines: 0     (was ~40 per evaluation)
warn lines:        1     (the wrong-type case, still surfaced)

Open questions

  • Severity for the wrong-type case. I chose warn to keep it visible without the stack trace. If you'd rather keep error there, a log-once/deduplicated variant would be needed to stop the volume tracking request rate — happy to add that instead.
  • The TARGETING_MATCH reason. As noted in [BUG] starts_with/ends_with logs an ERROR with a stack trace on every evaluation when the property is absent #2018, all three cases above report TARGETING_MATCH with no errorCode, so an operator error is indistinguishable from a legitimate else-branch match to SDKs and to validation tooling. That's a behaviour change rather than a logging fix, so I've deliberately left it out of this PR and am happy to follow up separately if you want to pursue it.

…on property

Fixes open-feature#2018.

A `starts_with` / `ends_with` rule referencing a context attribute the client
did not send logged at error level, with a stack trace, on every evaluation.
jsonLogic resolves a missing `var` to nil, and the parse helper could not tell
that apart from a wrong-typed operand, so both took the same error path.

Two properties of that path made it expensive. It runs per evaluation, so its
volume tracks request rate rather than the number of bad rules; and
zap.Config.Build attaches a stack trace at error level, turning each occurrence
into roughly forty log lines. A rule that ORs several version prefixes
multiplies again -- one of ours had eight starts_with calls, so a single bulk
OFREP evaluation produced several hundred lines.

That combination exhausted our organisation's daily log-index quota in about
nine minutes, stopping log indexing for every service we run until the quota
reset. The flag itself resolved correctly throughout; the entire cost was log
volume.

parseStringComparisonEvaluationData now returns a distinct sentinel when the
property operand is nil, letting the two cases be reported differently: an
absent attribute at debug, since referencing an optional attribute is ordinary
and the rule simply does not match, and a malformed rule at warn, which still
surfaces a real misconfiguration but without the stack trace. Both continue to
return nil to jsonLogic, so evaluation results are unchanged.

The two evaluators now share the comparison and logging path, which keeps that
policy in one place.

Verified against the reproduction in open-feature#2018: evaluation output is byte-identical
for absent, matching and wrong-typed operands, while the absent case now emits
no log line at default level and the wrong-typed case emits one warn with no
stack trace -- previously an error plus ~40 stack frames each.

Signed-off-by: Scott Holodak <scottt732@gmail.com>
@netlify

netlify Bot commented Aug 12, 2026

Copy link
Copy Markdown

Deploy Preview for polite-licorice-3db33c canceled.

Name Link
🔨 Latest commit 505efb7
🔍 Latest deploy log https://app.netlify.com/projects/polite-licorice-3db33c/deploys/6a7cdfa7ac81ea0009fb17b9

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 467ccf74-ef82-4377-a066-b4684492cbe6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

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.

[BUG] starts_with/ends_with logs an ERROR with a stack trace on every evaluation when the property is absent

1 participant