multica-ai/multica · error
read daemon checkout response: %w
Error message
read daemon checkout response: %w
What it means
runRepoCheckout wraps io.ReadAll(resp.Body) after a successful POST to the daemon checkout endpoint. This fails only on mid-body transport errors: the daemon (or loopback stack) closes the connection before the full response is delivered, or the 5-minute context is cancelled while the body is still streaming.
Source
Thrown at server/cmd/multica/cmd_repo.go:391
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Minute)
defer cancel()
client := &http.Client{}
checkoutURL := fmt.Sprintf("http://127.0.0.1:%s/repo/checkout", daemonPort)
var body []byte
for {
req, err := http.NewRequestWithContext(ctx, http.MethodPost, checkoutURL, bytes.NewReader(data))
if err != nil {
return fmt.Errorf("create daemon checkout request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("connect to daemon: %w", err)
}
body, err = io.ReadAll(resp.Body)
closeErr := resp.Body.Close()
if err != nil {
return fmt.Errorf("read daemon checkout response: %w", err)
}
if closeErr != nil {
return fmt.Errorf("close daemon checkout response: %w", closeErr)
}
if resp.StatusCode == http.StatusServiceUnavailable && resp.Header.Get("X-Multica-Retryable") == "repo-busy" {
delay := repoCheckoutRetryDelay(resp.Header.Get("Retry-After"), time.Now())
timer := time.NewTimer(delay)
select {
case <-ctx.Done():
timer.Stop()
return fmt.Errorf("connect to daemon: %w", context.Cause(ctx))
case <-timer.C:
continue
}
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("checkout failed: %s", string(body))
}View on GitHub (pinned to 2c0912b6ec)
Solutions
- Check daemon logs for a crash/restart at the same timestamp; restart the daemon and retry the checkout.
- If the cause is the 5-minute deadline, reduce checkout cost (shallow clone mode via MULTICA_REPO_CHECKOUT_MODE, narrower ref) or increase the CLI timeout.
- Retry the command — checkout endpoints that already prepared the workdir usually succeed or cleanly report state on the second attempt.
Defensive patterns
Strategy: retry
Try / catch
body, err := io.ReadAll(resp.Body)
if err != nil {
// transient mid-body failure: safe to retry the whole POST (checkout is keyed by url+workdir)
return fmt.Errorf("read daemon checkout response: %w", err)
} Prevention
- Monitor daemon memory so it is not OOM-killed mid-response.
- Keep responses small/fast to stay clear of the 5-minute read deadline.
- Treat read failures as retriable at the command level.
When it happens
Trigger: Daemon crashes or is killed while writing the response; the checkout response is large and slow enough that ctx (5-minute cap) expires mid-read; loopback connection reset (rare, e.g. socket closed by sandbox teardown).
Common situations: Daemon OOM-killed during a big checkout; container/namespace being torn down while the CLI still reads; response streaming blocked behind a slow git operation until the deadline hits.
Related errors
- connect to daemon: %w
- checkout failed: %s
- daemon exited during startup.
- repo is not configured for this workspace
- local_directory: local_path is empty
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/dd53bad1ebddb3c6.
Report an issue: GitHub.