{"record":{"id":"add45300c8a88af9","repo":"d2lang/d2","slug":"failed-to-wait-for-workers-w","errorCode":null,"errorMessage":"failed to wait for workers: %w","messagePattern":"failed to wait for workers: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/imgbundler/imgbundler.go","lineNumber":146,"sourceCode":"\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tselect {\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\tcase replc <- repl{\n\t\t\t\t\tfrom: img[0],\n\t\t\t\t\tto:   bundledImage,\n\t\t\t\t}:\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}()\n\n\tt := time.NewTicker(time.Second * 5)\n\tdefer t.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn svg, fmt.Errorf(\"failed to wait for workers: %w\", ctx.Err())\n\t\tcase <-t.C:\n\t\t\tl.Info(\"fetching images...\")\n\t\tcase repl, ok := <-replc:\n\t\t\tif !ok {\n\t\t\t\tif len(errhrefs) > 0 {\n\t\t\t\t\treturn svg, fmt.Errorf(\"%v\", errhrefs)\n\t\t\t\t}\n\t\t\t\treturn svg, nil\n\t\t\t}\n\t\t\tsvg = bytes.Replace(svg, repl.from, repl.to, -1)\n\t\t}\n\t}\n}\n\nfunc worker(ctx context.Context, l simplelog.Logger, inputPath string, href []byte, isRemote, cacheImages bool) ([]byte, error) {\n\tif cacheImages {\n\t\tif hit, ok := imgCache.Load(string(href)); ok {\n\t\t\treturn hit.([]byte), nil","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/imgbundler/imgbundler.go#L128-L164","documentation":"runWorkers orchestrates remote image fetching for SVG bundling and waits on a select loop. If the context is cancelled (deadline exceeded or explicitly cancelled) before all worker replies arrive on replc, it aborts with this wrapped ctx.Err(). The bundle ultimately fails rather than emitting an SVG with missing images.","triggerScenarios":"Calling bundle with a context whose deadline expires while image downloads are still in flight, or cancelling the context mid-run; also network stalls that keep workers from finishing within the budget.","commonSituations":"Slow/unreachable remote image hosts behind the URLs in the diagram, short context timeouts in CI, or large diagrams referencing many remote images.","solutions":["Increase the context deadline passed to bundle","Check that image URLs are reachable from the environment (proxy/firewall/DNS)","Pre-download or inline images (data: URIs) so workers finish quickly","Add retry/backoff for flaky hosts and inspect the wrapped ctx.Err() to confirm deadline vs cancel"],"exampleFix":"// before\nctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)","handlingStrategy":"try-catch","validationCode":"for _, u := range imageHrefs {\n    req, _ := http.NewRequestWithContext(ctx, http.MethodHead, u, nil)\n    if _, err := http.DefaultClient.Do(req); err != nil {\n        return fmt.Errorf(\"unreachable image %s: %w\", u, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"svg, err := imgbundler.Bundle(ctx, svgBytes)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        // retry with larger budget or pre-fetch images\n        log.Printf(\"bundle timed out: %v\", err)\n    }\n    return err\n}","preventionTips":["Give bundle a generous context timeout proportional to the number of images","Pre-download remote images or inline them as data: URIs","Verify proxy/firewall allows outbound requests in CI environments","Use ctx.Err() via errors.Is to distinguish timeout from cancellation"],"tags":["network","timeout","context","imgbundler"],"backgroundTag":"context-deadline-exceeded","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}