fix(sfs): guard against nil responses from the API - #1741
Open
devpie wants to merge 1 commit into
Open
Conversation
The generated SDK decoder returns early for an empty body, so Execute() hands back (nil, nil) for a 2xx response that carries no body, and a wait handler returns (nil, nil) when a single poll hits a retryable 502 or 504: WaiterHelper reports (true, nil, err), handleError swallows the retryable status and returns a nil error, and the loop then returns the nil response because it is already done. The SFS resources and data sources dereferenced those responses unchecked and crashed the provider process. Several of the checks meant to validate a response were themselves the dereference - they tested `x.Field == nil` without first testing `x == nil` - and in create the wait handler result was read by tflog.SetField one line before its guard. Reproduced for resourcepool Read with terraform-plugin-testing: a refresh against a 200 with an empty body panics with "invalid memory address or nil pointer dereference". The guards follow the shape already used in the package, one combined condition covering the response and the fields actually used. Two error titles in the resource pool and share update paths said "creating" and are corrected while their lines are touched. Left unchanged: export-policy and snapshot-policy already guard their responses, project-lock uses nil-receiver-safe getters, and every Delete discards the response, so the delete waiter's legitimate nil return on a 404 is harmless. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
relates to #1743
The SFS resources and data sources crash the provider process when the API answers with a body the SDK decodes to
nil. Found while writing tests for #1740; filed separately because it is an independent bug and touches five files.How a response becomes nil
Two routes, both without any cooperation from the SFS API:
if len(b) == 0 { return nil }, solocalVarReturnValuestays nil andExecute()returns(nil, nil).WaiterHelper.Waitreturnstrue, nil, errfor any fetcherror;
handleErrorsees a 502 or 504 inRetryHttpErrorStatusCodes, increments the counter and returns a nilerror; the loop then hits
if done { return res, nil }and hands back the nil response. Becausedoneis alreadytrue the retry never happens. This applies to the create and update wait handlers, not only to delete.
Reported upstream as core/wait: waiter returns (nil, nil) on a retryable 502/504 and never retries stackit-sdk-go#11084 with a runnable reproduction; this PR guards the call
sites so the provider reports a diagnostic instead of crashing while that is open.
What was wrong
The call sites dereferenced those responses unchecked. Several of the checks meant to validate a response were
themselves the dereference — they test
x.Field == nilwithout first testingx == nil:And in both create paths the wait handler result is read by
tflog.SetFieldone line before its guard.Reproduced for
resourcepoolRead with terraform-plugin-testing: refreshing against a 200 with an empty body panicswith
invalid memory address or nil pointer dereference. Both new tests panic without this change and pass with it.Scope
Fixed:
resourcepool/resource.go,resourcepool/datasource.go,share/resource.go,share/datasource.go,snapshots/datasource.go.Left alone on purpose:
export-policyalready guards its responses (resource.go:293, andmapFieldsnil-checks thewhole response for Read and Update),
snapshot-policynil-checks inmapFields,project-lockuses nil-receiver-safegetters, and every SFS
Deletediscards the response with_, err :=, so the delete waiter's legitimate(nil, nil)on a 404 is harmless. No SFS
ImportStatecalls the API.The guards follow the shape already used in the package — one combined condition covering the response and the fields
actually used, then
core.LogAndAddErrorandreturn. Two error titles in the resource pool and share update pathssaid "creating"; they are corrected while their lines are touched.
The two regression tests cover the two shapes (a plain response guard, and a guard that was itself the dereference).
The data source guards are mechanically identical to the tested resource guards and are not separately covered.
Checklist
make fmtexamples/directory) — not applicable, no configuration surface changesmake generate-docs(will be checked by CI) — no schema change, so no doc changemake test(will be checked by CI)make lint(will be checked by CI)🤖 Generated with Claude Code