Skip to content

docs(flags): return no payload for malformed JSON - #71

Merged
marandaneto merged 3 commits into
mainfrom
fix/malformed-flag-payloads
Sep 22, 2026
Merged

marandaneto merged 3 commits into
mainfrom
fix/malformed-flag-payloads

Conversation

@marandaneto

@marandaneto marandaneto commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Problem

The specs allowed malformed feature flag payloads to escape as raw strings. Cache and snapshot contracts also left different interpretations of failure handling. The Python PR #947 review confirms that malformed payloads should be treated as absent.

Changes

  • Require malformed, empty, and whitespace-only serialized payloads to return the known-flag/no-payload result rather than raw-string fallback. JavaScript and TypeScript use null for this case.
  • Apply failure isolation to single payload getters, structured results, bulk APIs, client caches, and server snapshots. Preserve evaluated flag values and healthy sibling payloads.
  • Keep valid serialized snapshot representations supported, but require JSON validation even when callers do not request typed decoding. Decoded-value getter contracts can require a return-type migration for APIs such as the deprecated Go getter.
  • Add caller-default and JSON-like already-decoded string cases. Explicitly cover cached, local, and remote paths using scenario-level platform tags and shared step wording.
  • Synchronize canonical specs and archive both completed OpenSpec changes. No active proposal remains.

Validation

  • All 62 canonical specs pass strict OpenSpec validation.
  • Five changed acceptance files parse and expand into 106 scenarios. Serialized-payload fixture JSON and scenario-level tags validate.
  • Follow-up deltas match the canonical requirements and reuse acceptance step text.
  • git diff --check passes.
  • Autoreview of 5dc16d4 against origin/main reports no actionable findings.
  • This repository contains contracts, not SDK implementations. No SDK runtime conformance is claimed.

@marandaneto
marandaneto requested a review from a team September 21, 2026 09:59
@marandaneto
marandaneto marked this pull request as ready for review September 21, 2026 09:59
@marandaneto
marandaneto requested a review from a team as a code owner September 21, 2026 09:59
@marandaneto

Copy link
Copy Markdown
Member Author

SDK implementation audit / update checklist

Audited the fetched default branches of all 17 repositories against this PR at 29db3d84a04c7fc454833bd4f913bab61540239f, including single-payload, structured-result, and bulk/snapshot APIs where available. 13 repositories need implementation fixes or dependency updates. Checkboxes below track follow-up work; they do not imply an implementation PR already exists.

SDKs needing updates

  • posthog-android: mobile/core normalization retains malformed, empty, and whitespace raw strings; serialized null also remains a string. Source. The Java/server SDK in this repository independently exposes unvalidated raw payload strings through its snapshot getter; its intentionally raw API needs compatibility consideration.
  • posthog-ios: parsing failures return the original string, including empty/whitespace input. Source.
  • posthog-flutter: inherits Android/iOS defects; update native minimum dependencies after fixes. No separate Dart JSON-parser fix indicated. Web delegates to the host-selected JS SDK, so its behavior depends on that version. Android bridge, web bridge.
  • posthog-kmp: inherits defects from pinned Android/JVM, iOS, and JS dependencies. Update dependencies after upstream fixes; do not add a second decoding pass in wrappers. Pinned versions.
  • posthog-js: shared core returns raw strings on parse failure, affecting browser, React Native, Web, and dependent integrations. Node has independent fallback code, so a core-only fix is insufficient. Cover local/remote, single/result/bulk/snapshot paths. Core, Node single/result, Node snapshot.
  • posthog-dotnet: unguarded payload parsing can throw or discard evaluated flag results and healthy siblings. Isolate failure to the affected payload rather than replacing the flag/batch. Constructors, batch conversion.
  • posthog-go: public payload APIs expose serialized strings without validating their JSON contents. Its deliberately raw-string API needs an API-compatible migration decision, not just a catch/fallback change. Normalization, snapshot getter.
  • posthog-elixir: both local and remote parse failures return the original string. Remote, local.
  • posthog-python: continue existing fix: Return None for malformed feature flag payloads posthog-python#947. At inspected head 37812035013ae867f66697662fb5285608d7ff8b, it fixes single-result/snapshot decoding, but legacy get_all_flags_and_payloads still exposes raw payload strings in local and remote paths. Extend that PR to cover bulk rather than opening a duplicate. Unchanged remote projection on PR head.
  • posthog-ruby: structured results retain malformed/whitespace strings; legacy single and bulk paths return unparsed payloads. Result decoder, single/bulk.
  • posthog-rs: snapshots return raw strings on parse failure; legacy single/bulk normalization skips inner payload decoding. Cover async and blocking APIs. Snapshot decoder, legacy normalization.
  • posthog-unity: string getters bypass decoding; JSON helper retains malformed strings and does not correctly decode all valid non-object JSON. Strict arbitrary-JSON handling is needed, not only catch-to-null. Payload getters, JSON helper.
  • posthog-godot: failed JSON parsing leaves the raw string in the structured result/payload getter. Source.

No direct malformed-string fix identified

Repository Finding
posthog-php Malformed, empty, and whitespace serialized payloads already become null, preserving flag results and healthy siblings. Remote decoding, local decoding. This is a pass for the requested malformed-string return behavior, not full spec certification. The legacy remote path separately lacks an is_string guard for already-decoded non-string inputs.
posthog-pi Telemetry/MCP integration; no exposed SDK feature-flag payload getter. Inspected 29993f4a868b3084052ad16558bd6a564faf348f.
posthog-openclaw Telemetry integration; no exposed feature-flag payload getter. Inspected c56797c2e3e08207077b2671007c0a77d68be3dc.
posthog-opencode Telemetry integration; no exposed feature-flag payload getter. Inspected c993b0846aa73fae099171a298b4edf03a89e89e.

Shared acceptance coverage / implementation cautions

  • posthog-sdk-test-harness: add payload API actions/assertions and malformed/empty/whitespace scenarios; current fetched default branch (c32f4959240967f1ee89e58490fc9d3beaa1401f) lacks this payload-getter contract coverage.
  • Exercise single/result/bulk APIs against malformed input, raw empty input, and whitespace, across remote/cache/local paths where supported. Assert no raw fallback or caller-visible exception, unchanged flag value/enabled/variant, and healthy sibling preservation.
  • Include valid JSON strings, JSON "", false, 0, null, arrays, objects, and already-decoded string round trips. Do not parse every string indiscriminately: some bootstrap/override/cache paths already contain decoded strings. Blanket catch-to-null changes would destroy legitimate string payloads there. JS also has truthiness-based false/zero/empty-string loss to address.
  • Update existing tests that explicitly expect invalid JSON to survive as raw strings. Ensure failures use SDK diagnostics as required by the spec.

Validation scope: read-only inspection of immutable fetched commits, relevant test inspection, focused runtime reproductions (Python, Ruby, PHP, JS, Java/Gson, Swift), and selected Go tests. This was not a full SDK integration-suite run; Rust execution was blocked by an unavailable offline dependency, and Unity/Godot conclusions are source-based. No SDK source changes or implementation PRs were made by this audit.

@haacked haacked 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.

Looks good overall. Two blocking issues inline, the rest are suggestions and one question.

Comment thread openspec/specs/get-feature-flag-payload/spec.md
Comment thread openspec/specs/get-feature-flag-payload/spec.md
Comment thread acceptance/public/get-feature-flags-and-payloads.feature Outdated
Comment thread acceptance/public/get-feature-flag-result.feature Outdated
Comment thread acceptance/public/get-feature-flag-payload.feature Outdated
Comment thread openspec/specs/get-feature-flag-payload/spec.md Outdated
Comment thread openspec/specs/get-feature-flag-payload/spec.md Outdated
Comment thread acceptance/public/get-feature-flag-payload.feature Outdated
Comment thread acceptance/public/get-feature-flag-result.feature
@marandaneto
marandaneto merged commit 68470d0 into main Sep 22, 2026
10 checks passed
@marandaneto
marandaneto deleted the fix/malformed-flag-payloads branch September 22, 2026 06:59
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