Go: cancel the rest of a batch after the first 402 on Sume
A Go fan-out that stops launching Wan 3.0 submits the moment one returns 402 insufficient_credits, using context.WithCancel and a 2-slot semaphore.

When the balance runs out in the middle of a batch, every remaining submit will fail the same way. Sume's docs say a generation submit returns 402 insufficient_credits before provider work starts, so the failed calls cost nothing, but sending 40 more of them is noise and rate-limit budget you do not need to spend.
The Go program below launches shots through a 2-slot semaphore and keeps a context.WithCancel. The goroutine that sees a 402 calls stop(), and the launch loop checks the context before starting the next shot. Shots that were never sent stay at 0 in the result slice. It was run with a local stand-in that returns 202 four times and then 402; it was not pointed at the live API.
The code (30 lines)
The context also goes into NewRequestWithContext, so a request that has not been written yet is abandoned when the batch is stopped. Requests already on the wire finish normally.
package main
import ("context"; "fmt"; "net/http"; "os"; "strings"; "sync")
func submit(ctx context.Context, i int) int {
body := fmt.Sprintf(`{"model":"wan-3.0","prompt":"shot %d","duration":5,"resolution":"480p"}`, i)
req, _ := http.NewRequestWithContext(ctx, "POST", os.Getenv("SUME_BASE")+"/v1/videos", strings.NewReader(body))
req.Header.Set("x-api-key", os.Getenv("SUME_API_KEY"))
req.Header.Set("content-type", "application/json")
req.Header.Set("Idempotency-Key", fmt.Sprintf("trailer-v4-shot-%02d", i))
res, err := http.DefaultClient.Do(req)
if err != nil { return 0 }
res.Body.Close()
return res.StatusCode
}
func main() {
ctx, stop := context.WithCancel(context.Background())
codes, sem := make([]int, 12), make(chan struct{}, 2)
var wg sync.WaitGroup
for i := range codes {
sem <- struct{}{}
if ctx.Err() != nil { <-sem; break } // a 402 already told us the balance is gone
wg.Add(1)
go func() {
defer wg.Done()
defer func() { <-sem }()
if codes[i] = submit(ctx, i); codes[i] == 402 { stop() }
}()
}
wg.Wait()
fmt.Println(codes) // 0 = never sent
}What the run printed
With a stub that accepted four submits and then answered 402, the result slice looked like this.
[202 202 202 202 402 0 0 0 0 0 0 0]After the top-up
Zeros mean never sent, so those are the shots to resend once credits are added. The four accepted shots keep their jobs; resubmitting them with the same Idempotency-Key returns the originals instead of billing twice. Check GET /v1/balance before restarting rather than finding the next 402 the hard way.
Sources
Related posts
More in Developers
- Go: a context timeout stops waiting on a Sume job, not the job
context.WithTimeout cancels your status read, never the generation. A 29-line Go loop shows the DeadlineExceeded branch, and why the job id must be stored.
- Go client for a Sume bulk queue: transient polls and exit status
Create a Sume bulk queue from Go, poll the status_url with a doubling gap, treat 429 and 503 as transient, and exit 1 on failed items. Standard library only.
- Go httptest: a four-case table test for a Sume webhook handler
Sign a fixture body with sume-v1 HMAC, then assert 204 for a valid call and 401 for a stale timestamp, an empty server secret and a wrong secret.
- Go MaxBytesReader: cap a Sume webhook body at 1 MiB, 413 before HMAC
A webhook endpoint that reads an unbounded body invites memory abuse. A Go handler with http.MaxBytesReader, an HMAC check and a refusal of empty secrets.
Written by Sume