Skip to content

fix: a nested attribute is held as a value that can be unknown - #115

Merged
ShocOne merged 2 commits into
mainfrom
fix/nested-attributes-hold-unknown
Aug 25, 2026
Merged

fix: a nested attribute is held as a value that can be unknown#115
ShocOne merged 2 commits into
mainfrom
fix/nested-attributes-hold-unknown

Conversation

@ShocOne

@ShocOne ShocOne commented Aug 25, 2026

Copy link
Copy Markdown
Member

Found by running the generated acceptance tests against a live ThousandEyes tenant. Every one of the 35 generated resources failed at apply, all with the same error. The provider built, vetted, passed 213 unit tests and regenerated byte-identical in that state.

Error: Value Conversion Error
Received unknown value, however the target type cannot handle unknown values.
  Path: links
  Target Type: *thousandeyescredential.CredentialLinksModel
  Suggested Type: basetypes.ObjectValue

Cause

A nested attribute's model field was the generated struct — *XxxModel for an object, []XxxModel for a list. Neither can represent unknown, which is exactly what a Computed attribute carries in the plan before the API has answered. links and coordinates are server-returned HATEOAS blocks, so they are Computed on every resource that has one.

Scale: 240 such fields in ThousandEyes, 959 in GitHub, plus 29 computed ListNestedAttributes carrying the same latent fault.

Fix

Before After
Object field *XxxModel types.Object
List field []XxxModel types.List
Object type generated XxxModelAttrTypes() beside each nested struct
State building assign pointer ObjectValueFrom / ListValueFrom
Construction dereference struct As / ElementsAs into the struct, then read
Validator absence test == nil .IsNull()

The mappers now return diag.Diagnostics, because building an object can report a mismatch against its declared types. The nested structs are still generated — they are what the object is built from and decoded back into.

The curated fixture caught something I had wrong: I had concluded from both pilots that nested objects are read-only and construction needed no change. That was true only because binding had pruned every writable nested object in those two documents. The fixture has writable ones, and construct.go failed to compile until it decoded through As/ElementsAs.

Also here

internal/audit/plan panicked with a nil dereference before its first request, on the same live run. A singleton is classified a resource but has no create operation — it is written through its update call — so the create slot is empty. It is now refused with a reason, and the rest of the run stands.

Verification

go test ./internal/...            all pass
coverage_gate.sh                  91.3% total
repo_hygiene_gate.sh              pass
golangci-lint run                 0 issues

Both pilots regenerate byte-identical and pass postcheck (go mod tidy, build, vet):

files resources / datasources / list / actions pointer-to-struct fields
ThousandEyes 1931 35 / 96 / 30 / 53 0 (was 240)
GitHub 4442 59 / 323 / 29 / 77 0 (was 959)

Live re-run against the tenant: the conversion error is gone — 0 occurrences, down from 35. The provider now reaches the API, and fails there instead (HTTP 400 on create), which is a different and later problem: the generated fixtures do not yet satisfy what the API requires.

🤖 Generated with Claude Code

ShocOne and others added 2 commits August 25, 2026 13:20
Every generated resource failed at apply. A nested attribute's model field
was the generated struct — a pointer for an object, a slice for a list — and
neither can represent unknown, which is what a Computed attribute carries in
the plan before the API has answered:

  Received unknown value, however the target type cannot handle unknown
  values. Path: links  Target Type: *CredentialLinksModel

The field is now types.Object or types.List, and each nested struct gains an
AttrTypes function beside it naming the object type it maps onto. State
building goes through ObjectValueFrom and ListValueFrom, so the mappers
return diagnostics; construction decodes the plan value with As and
ElementsAs before reading its fields; and the conditional validators test
absence with IsNull, which now applies to every attribute alike.

Found by running the generated acceptance tests against a live tenant. The
provider built, vetted and passed 213 unit tests in that state, and could not
apply a single resource.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The first live audit run panicked before its first request. A singleton is
classified a resource without a create operation — it is written through its
update call — so the create slot is empty and deriving its plan dereferenced
nil.

It is now refused with a reason, and the rest of the run stands: an entity
the audit cannot exercise is a fact about that entity.

The test pins the shape that crashed rather than the line that fixed it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ShocOne
ShocOne merged commit 38599d3 into main Aug 25, 2026
2 checks passed
@ShocOne
ShocOne deleted the fix/nested-attributes-hold-unknown branch August 25, 2026 15:34
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.

1 participant