Repository navigation
builder: retry droplet create when new SSH keys are not yet visible - #176
Merged
Merged
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
anup-deka
reviewed
Oct 13, 2026
Use a fixed 1m retry window for droplet create, IPv4 polling, and temporary key deletion instead of state_timeout (default 6m). Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Merged
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.
Issue
POST /v2/account/keyssucceeds, thenPOST /v2/dropletsimmediately uses the numeric key ID and returns422 … are invalid key identifiers for Droplet creation.DELETE /v2/account/keys/{id}then 404s; the key later exists and is left behind (replica lag between key write and droplet-create validation).HTTPRetryMaxonly retries 429/5xx, not 422.active, a singleGETcan still have emptynetworks.v4(IPv4 address not found for droplet). PollingGET /v2/account/keys/{id}is not a valid consistency check: Keys GET can succeed while droplet create still 422s.Solution
Droplets.Createonly on 422invalid key identifierswith exponential backoff (1s → cap 15s), overall deadlinestate_timeout(default 6m). Other 422s fail immediately.Keys.DeleteByIDon 404 with the same backoff so lag does not orphan keys. StorekeyIdas soon as create succeeds so cleanup still runs if a later step fails.active, poll until the expected public/private IPv4 exists (retry empty networks and 404). Do not poll Keys GET or use a blind 60s sleep.Reproduction
ssh_key_id/ssh_private_key_file).422 … are invalid key identifiers; key delete 404s.Testing
go test ./builder/digitalocean/unit tests for backoff cap, retryable vs permanent errors, 422/404 classifiers, and IPv4 selection.api.digitalocean.com: 40 sequential key→droplet creates did not 422 (lag is load-dependent).tor1/sfo3/nyc3all 422'd on first droplet create; backoff recovered all 8 (reproduced_422=8,recovered_with_backoff=8). Several also retried empty IPv4 afteractive. All droplets/keys deleted.CI pins
macos-14instead ofmacos-latestbecause GitHub’s latest macOS is 15, and Go 1.21/1.22-racebinaries lackLC_UUIDsodyldrejects them.The required check
test (1.21.x, macos-latest)will stay pending on this PR until branch protection is updated tomacos-14(or the pin is reverted).Test plan
go test ./builder/digitalocean/RUN_PROD_API_TEST=1 DIGITALOCEAN_TOKEN=$(doctl auth token) PROD_API_ATTEMPTS=64 PROD_API_PARALLEL=8 go test ./builder/digitalocean -run TestProdSSHKeyLagAgainstAPI -v -timeout 25mssh_key_id) against a real accountFixes ESC-26331
Made with Cursor