Skip to content

fix(untracked): stream include metadata null after update#69

Merged
lwargin-circle merged 11 commits into
mainfrom
fix/stream-include-metadata-null-after-update
Apr 21, 2026
Merged

fix(untracked): stream include metadata null after update#69
lwargin-circle merged 11 commits into
mainfrom
fix/stream-include-metadata-null-after-update

Conversation

@lwargin-circle

@lwargin-circle lwargin-circle commented Apr 16, 2026

Copy link
Copy Markdown
Contributor

Root Cause

QuickNode silently removed include_stream_metadata from their Streams API — a breaking change
with no announcement:

Before Now
GET /streams/:id response returned field null (confirmed via curl)
POST/PATCH request body accepted removed from live OpenAPI spec
Official docs documented no longer listed

Our vendored streams-openapi.json still had the field (last regenerated Feb 2026), causing
the provider to send a field the API no longer accepts and expect a value the API never returns.

Failures

1. terraform apply — "Provider produced inconsistent result after apply"

Any update to a quicknode_stream (e.g. changing notification_email) triggered:
.include_stream_metadata: was cty.StringVal("header"), but now null.
The provider did a GET after the PATCH to refresh state; the absent field left null in the
freshly initialised StreamResourceModel{}, overwriting the planned "header".

2. terraform plan — phantom diffs on every run

Every Read() call overwrote the state value with null, queuing a spurious update
on every plan/apply cycle.

Changes

File Change
api/streams/streams-openapi.json Updated from live QuickNode API (make vendor)
api/streams/streams.gen.go Regenerated — IncludeStreamMetadata removed from CreateStreamDto / UpdateStreamDto
internal/provider/stream_resource.go Remove field from Create/Update requests; schema Required→Optional + deprecation warning; fallback in Read and post-update Read

Schema change: RequiredOptional + deprecated

Existing configs that still set include_stream_metadata = "header" will:

  • Continue to parse without error (field is Optional)
  • Show a deprecation warning during terraform plan
  • No longer send the value to the API (silently dropped)
  • Preserve the existing state value via fallback (no phantom diffs)

Users can remove the field from their configs at their own pace.

Testing

  • go build ./... — compiles cleanly
  • go test ./internal/... — all tests pass

lwargin-circle and others added 3 commits April 16, 2026 15:39
…read-after-update

When updating a quicknode_stream resource, the provider calls readStreamFromAPI
(a GET) after the PATCH to refresh computed fields. For certain stream types the
QuickNode API does not include include_stream_metadata in the GET response.

Because readStreamFromAPI initialised a blank StreamResourceModel{} and only
populated fields that were present in the response, the field was left as null.
The Update function then assigned that null back to the plan, causing Terraform
to report "Provider produced inconsistent result after apply" for every affected
stream — even when the field itself was not part of the change.

Fix: add an optional fallback parameter to readStreamFromAPI. When the API
response omits include_stream_metadata, the fallback (plan) value is preserved
instead of letting the field become null. All existing callers are unaffected
(no fallback passed). The Update caller now passes &plan so any API-omitted
field retains its planned value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The QuickNode GET /streams/:id endpoint no longer returns
include_stream_metadata in its response (confirmed via curl; field also
removed from official API docs). This caused two separate issues:

1. Read() — on every terraform plan/apply the field was read back as null
   from the API, overwriting the known state value and triggering a phantom
   diff that would attempt a no-op update on every cycle.

2. Update() — after a successful PATCH the provider refreshed state via the
   same GET call; the null field was then assigned to the plan, causing
   Terraform to fail with "Provider produced inconsistent result after apply".

Fix: pass the current state (Read) or current plan (Update) as a fallback
to readStreamFromAPI so that any field absent from the API response is
preserved from the known-good value rather than silently becoming null.
All other callers (Create, pre-update status check) remain unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
QuickNode silently removed include_stream_metadata from their Streams API:
- No longer returned in GET /streams/:id responses (confirmed via curl)
- No longer accepted in POST/PATCH request bodies (removed from live OpenAPI spec)
- No longer documented in the official API docs

This caused two Terraform failures for any stream that was updated:

1. "Provider produced inconsistent result after apply" — after a PATCH the
   provider called GET to refresh state; the absent field left a null in the
   freshly initialised model, overwriting the planned value of "header".

2. Phantom diffs — every terraform plan overwrote the state value with null,
   queuing a spurious update on every cycle.

Changes in this commit:
- Update vendored streams-openapi.json from the live QuickNode API endpoint
  (make vendor: curl https://api.quicknode.com/streams/rest/openapi.json)
- Regenerate streams.gen.go — IncludeStreamMetadata removed from
  CreateStreamDto and UpdateStreamDto generated types
- Remove IncludeStreamMetadata from Create and Update API request bodies in
  stream_resource.go (field no longer in API contract)
- Mark schema field include_stream_metadata as Optional + deprecated so that
  existing configurations continue to parse without error while users migrate
  away from the field
- Preserve fallback in readStreamFromAPI (Read + Update) so that existing
  state values are not silently nullified by the absent GET response field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Apr 16, 2026

Copy link
Copy Markdown

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 2 package(s) with unknown licenses.
See the Details below.

License Issues

go.mod

PackageVersionLicenseIssue Type
github.com/cloudflare/circl1.6.3NullUnknown License

.github/workflows/test.yml

PackageVersionLicenseIssue Type
aquasecurity/trivy-action57a97c7e7821a5776cebc9bb87c984fa69cba8f1NullUnknown License
Allowed Licenses: BSD-1-Clause, BSD-2-Clause, BSD-3-Clause, MIT, MIT-0, Apache-1.1, Apache-2.0, Artistic-1.0, Artistic-2.0, PHP-3.0, PHP-3.01, PSF-2.0, Zlib, zlib-acknowledgement, BSL-1.0, OpenSSL, WTFPL, CC0-1.0, CC-PDDC, CC-BY-1.0, CC-BY-2.0, CC-BY-2.5, CC-BY-3.0, CC-BY-4.0, Unlicense, ISC, BlueOak-1.0.0, BSD-2-Clause-Patent, ADSL, Apache-2.0, APAFML, BSD-1-Clause, BSD-2-Clause, BSD-2-Clause-FreeBSD, BSD-2-Clause-NetBSD, BSD-2-Clause-Views, BSL-1.0, DSDP, ECL-1.0, ECL-2.0, ImageMagick, ISC, Linux-OpenIB, MIT, MIT-Modern-Variant, MS-PL, MulanPSL-1.0, Mup, PostgreSQL, Spencer-99, UPL-1.0, Xerox, 0BSD, AFL-1.1, AFL-1.2, AFL-2.0, AFL-2.1, AFL-3.0, AMDPLPA, AML, AMPAS, ANTLR-PD, ANTLR-PD-fallback, Apache-1.0, Apache-1.1, Artistic-2.0, Bahyph, Barr, BSD-3-Clause, BSD-3-Clause-Attribution, BSD-3-Clause-Clear, BSD-3-Clause-LBNL, BSD-3-Clause-Modification, BSD-3-Clause-No-Nuclear-License-2014, BSD-3-Clause-No-Nuclear-Warranty, BSD-3-Clause-Open-MPI, BSD-4-Clause, BSD-4-Clause-Shortened, BSD-4-Clause-UC, BSD-Source-Code, bzip2-1.0.5, bzip2-1.0.6, CC0-1.0, CNRI-Jython, CNRI-Python, CNRI-Python-GPL-Compatible, Cube, curl, eGenix, Entessa, FTL, HTMLTIDY, IBM-pibs, ICU, Info-ZIP, Intel, JasPer-2.0, Libpng, libpng-2.0, libtiff, LPPL-1.3c, MIT-0, MIT-advertising, MIT-open-group, MIT-CMU, MIT-enna, MIT-feh, MITNFA, MTLL, MulanPSL-2.0, Multics, Naumen, NCSA, Net-SNMP, NetCDF, NTP, OLDAP-2.0, OLDAP-2.0.1, OLDAP-2.1, OLDAP-2.2, OLDAP-2.2.1, OLDAP-2.2.2, OLDAP-2.3, OLDAP-2.4, OLDAP-2.5, OLDAP-2.6, OLDAP-2.7, OLDAP-2.8, OML, OpenSSL, PHP-3.0, PHP-3.01, Plexus, PSF-2.0, Python-2.0, Ruby, Saxpath, SGI-B-2.0, SMLNJ, SWL, TCL, TCP-wrappers, Unicode-DFS-2015, Unicode-DFS-2016, Unlicense, VSL-1.0, W3C, X11, XFree86-1.1, Xnet, xpp, Zlib, zlib-acknowledgement, ZPL-2.0, ZPL-2.1, AAL, Adobe-2006, Afmparse, Artistic-1.0, Artistic-1.0-cl8, Artistic-1.0-Perl, Beerware, blessing, Borceux, CECILL-B, ClArtistic, Condor-1.1, Crossword, CrystalStacker, diffmark, DOC, EFL-1.0, EFL-2.0, Fair, FSFUL, FSFULLR, Giftware, HPND, IJG, Leptonica, LPL-1.0, LPL-1.02, MirOS, mpich2, NASA-1.3, NBPL-1.0, Newsletr, NLPL, NRL, OGTSL, OLDAP-1.1, OLDAP-1.2, OLDAP-1.3, OLDAP-1.4, psutils, Qhull, Rdisc, RSA-MD, Spencer-86, Spencer-94, TU-Berlin-1.0, TU-Berlin-2.0, Vim, W3C-19980720, W3C-20150513, Wsuipa, WTFPL, xinetd, Zed, Zend-2.0, ZPL-1.1
Excluded from license check: pkg:golang/github.com/hashicorp/terraform-plugin-framework@v1.15.0, pkg:golang/github.com/hashicorp/terraform-plugin-go@v0.27.0, pkg:golang/github.com/hashicorp/terraform-registry-address@v0.2.5, pkg:golang/google.golang.org/protobuf, pkg:golang/golang.org/x/crypto, pkg:golang/golang.org/x/mod, pkg:golang/golang.org/x/net, pkg:golang/golang.org/x/sync, pkg:golang/golang.org/x/sys, pkg:golang/golang.org/x/text, pkg:golang/golang.org/x/time, pkg:golang/golang.org/x/tools, pkg:golang/honnef.co/go/tools

OpenSSF Scorecard

PackageVersionScoreDetails
actions/aquasecurity/trivy-action 57a97c7e7821a5776cebc9bb87c984fa69cba8f1 🟢 6.7
Details
CheckScoreReason
Code-Review🟢 9Found 15/16 approved changesets -- score normalized to 9
Maintained🟢 1014 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 10
Binary-Artifacts🟢 10no binaries found in the repo
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 7detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Security-Policy⚠️ 0security policy file not detected
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
gomod/github.com/cloudflare/circl 1.6.3 UnknownUnknown
gomod/google.golang.org/genproto/googleapis/rpc 0.0.0-20251202230838-ff82c1b0f217 🟢 6.7
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Maintained🟢 1030 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Code-Review🟢 10all changesets reviewed
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Security-Policy🟢 10security policy file detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Binary-Artifacts🟢 10no binaries found in the repo
Signed-Releases⚠️ -1no releases found
Branch-Protection🟢 8branch protection is not maximal on development and all release branches
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST🟢 5SAST tool is not run on all commits -- score normalized to 5
gomod/google.golang.org/grpc 1.79.3 🟢 8.5
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 3 issue activity found in the last 90 days -- score normalized to 10
Packaging⚠️ -1packaging workflow not detected
Code-Review🟢 10all changesets reviewed
Security-Policy🟢 9security policy file detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Binary-Artifacts🟢 10no binaries found in the repo
License🟢 10license file detected
Fuzzing🟢 10project is fuzzed
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
Signed-Releases⚠️ -1no releases found
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
SAST🟢 7SAST tool detected but not run on all commits
gomod/google.golang.org/protobuf 1.36.10 UnknownUnknown

Scanned Files

  • .github/workflows/test.yml
  • go.mod

…eptance test

Create() also called readStreamFromAPI without a fallback, causing
include_stream_metadata to be overwritten with null after stream creation.
Pass the plan as fallback so the config value is preserved in state.

Update the acceptance test:
- Remove TestCheckResourceAttr for include_stream_metadata (API no longer
  returns it, value comes from config fallback not API verification)
- Add ImportStateVerifyIgnore for include_stream_metadata (import has no
  prior state to fall back to, so the field will be null after import)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@lwargin-circle lwargin-circle marked this pull request as ready for review April 16, 2026 14:26
@lwargin-circle lwargin-circle changed the title Fix/stream include metadata null after update fix(untracked): stream include metadata null after update Apr 21, 2026
- Pin aquasecurity/trivy-action to v0.35.0 so setup-trivy resolves (v0.2.2 tag removed).
- Run tfplugindocs output for stream.md (include_stream_metadata optional/deprecated).

Made-with: Cursor
setup-trivy uses install.sh which fetches binaries via get.trivy.dev; Circle
hosted runners hit curl exit 6 (host resolve). Install the release tarball
from github.com and skip the composite setup step.

Made-with: Cursor
Resolves authorization bypass reported by Trivy/govulncheck so CI
trivy-scan passes with exit-code 1.

Made-with: Cursor
Code scanning UI is not available without GHAS; upload-sarif was misleading.
Use scanners=vuln, exit-code 0 (non-blocking), upload SARIF as workflow
artifact, and print a table to the job summary for review.

Made-with: Cursor
@lwargin-circle lwargin-circle force-pushed the fix/stream-include-metadata-null-after-update branch from 8a9532b to 6dec937 Compare April 21, 2026 20:35
Trivy reported LOW CVE in circl v1.6.1; fixed upstream in v1.6.3.
Ignore local trivy-results.sarif; restore Trivy exit-code 1 on findings.

Made-with: Cursor
google.golang.org/protobuf is BSD-3-Clause plus Google's golang patent
grant; ScanCode reports it as incompatible without allowlisting, same as
golang.org/x modules already listed here.

Made-with: Cursor
Public workflow artifacts are downloadable by anyone; keep the job
summary table for public repos and attach SARIF only when private.

Made-with: Cursor
@lwargin-circle lwargin-circle merged commit 8412cc4 into main Apr 21, 2026
13 checks passed
@lwargin-circle lwargin-circle deleted the fix/stream-include-metadata-null-after-update branch April 21, 2026 21:11
lwargin-circle pushed a commit that referenced this pull request Apr 21, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.7.2](v0.7.1...v0.7.2)
(2026-04-21)


### Bug Fixes

* **untracked:** stream include metadata null after update
([#69](#69))
([8412cc4](8412cc4))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
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.

2 participants