{"record":{"id":"cd6bae942bba22ac","repo":"multica-ai/multica","slug":"repository-is-busy-with-another-operation-retry-l","errorCode":null,"errorMessage":"repository is busy with another operation; retry later","messagePattern":"repository is busy with another operation; retry later","errorType":"http","errorClass":null,"httpStatus":503,"severity":"warning","filePath":"server/internal/daemon/health.go","lineNumber":301,"sourceCode":"\t\t\tIsolatedGitMetadata: req.CheckoutMode == repoCheckoutModeIsolated,\n\t\t}\n\t\tif req.RetryBusy {\n\t\t\tparams.LockWaitTimeout = repoCheckoutLockWaitTimeout\n\t\t}\n\t\tvar result *repocache.WorktreeResult\n\t\tvar err error\n\t\tif cache, ok := d.repoCache.(interface {\n\t\t\tCreateWorktreeContext(context.Context, repocache.WorktreeParams) (*repocache.WorktreeResult, error)\n\t\t}); ok {\n\t\t\tresult, err = cache.CreateWorktreeContext(r.Context(), params)\n\t\t} else {\n\t\t\tresult, err = d.repoCache.CreateWorktree(params)\n\t\t}\n\t\tif err != nil {\n\t\t\tif errors.Is(err, repocache.ErrRepoBusy) && req.RetryBusy {\n\t\t\t\tw.Header().Set(repoCheckoutRetryHeader, repoCheckoutRetryValueBusy)\n\t\t\t\tw.Header().Set(\"Retry-After\", fmt.Sprintf(\"%.0f\", repoCheckoutRetryAfter.Seconds()))\n\t\t\t\thttp.Error(w, \"repository is busy with another operation; retry later\", http.StatusServiceUnavailable)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif r.Context().Err() != nil {\n\t\t\t\td.logger.Debug(\"repo checkout cancelled\", \"url\", req.URL, \"error\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\td.logger.Error(\"repo checkout failed\", \"url\", req.URL, \"error\", err)\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\tjson.NewEncoder(w).Encode(result)\n\t}\n}\n","sourceCodeStart":283,"sourceCodeEnd":317,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/daemon/health.go#L283-L317","documentation":"HTTP 503 returned when the underlying repocache reports ErrRepoBusy (another operation holds the repository's lock — e.g. a concurrent clone/fetch/worktree operation on the same repo) and the request opted in with retry_busy=true. The response includes a retry header and a Retry-After value so callers can back off and retry the same request.","triggerScenarios":"POST repo checkout with retry_busy=true while another checkout, fetch, or clone is in flight for the same repository; the repo cache serializes per-repo operations and the second caller gets ErrRepoBusy.","commonSituations":"Multiple agents/tasks checking out the same repo concurrently; a long initial clone blocking subsequent checkouts; CI-style parallel runs against one repository.","solutions":["Honor the Retry-After header and resend the identical request after the indicated delay.","Reduce concurrency: queue checkouts per repository instead of firing them in parallel.","If checkouts of the same repo are frequent, pre-warm the cache (trigger one clone) so later checkouts are short worktree operations less likely to collide.","If busy errors persist far beyond Retry-After, inspect daemon logs for a stuck operation holding the repo lock."],"exampleFix":"// before: fire-and-forget concurrent checkouts\nfor _, t := range tasks { go checkout(t) } // some get 503 busy\n\n// after: honor Retry-After and retry\nresp, err := client.Do(req)\nif resp.StatusCode == http.StatusServiceUnavailable {\n    ra, _ := strconv.Atoi(resp.Header.Get(\"Retry-After\"))\n    time.Sleep(time.Duration(ra) * time.Second)\n    resp, err = client.Do(req)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt := 0; attempt < max; attempt++ {\n    resp, _ := client.Do(req.Clone(ctx))\n    if resp.StatusCode != http.StatusServiceUnavailable { break }\n    ra, _ := strconv.Atoi(resp.Header.Get(\"Retry-After\"))\n    select {\n    case <-time.After(time.Duration(ra) * time.Second):\n    case <-ctx.Done(): return ctx.Err()\n    }\n}","preventionTips":["Always send retry_busy=true when you can tolerate a retry.","Serialize checkouts per repository client-side with a keyed mutex/queue.","Pre-warm the repo cache after clone so later operations are short."],"tags":["daemon","git","concurrency","retry","http-503"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}