{"record":{"id":"528cede9fd1fbe78","repo":"go-task/task","slug":"context-cancelled-while-waiting-for-repository-loc","errorCode":null,"errorMessage":"context cancelled while waiting for repository lock: %w","messagePattern":"context cancelled while waiting for repository lock: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"taskfile/node_git.go","lineNumber":141,"sourceCode":"// The cache directory is /tmp/task-git-repos/{cache_key}/\nfunc (node *GitNode) getOrCloneRepo(ctx context.Context) (string, error) {\n\tcacheKey := node.repoCacheKey()\n\n\trepoMutex := globalGitRepoCache.getLockForRepo(cacheKey)\n\trepoMutex.Lock()\n\tdefer repoMutex.Unlock()\n\n\tcacheDir := filepath.Join(os.TempDir(), \"task-git-repos\", cacheKey)\n\n\t// Check cache FIRST - if already cloned, no network needed, timeout irrelevant\n\tgitDir := filepath.Join(cacheDir, \".git\")\n\tif _, err := os.Stat(gitDir); err == nil {\n\t\treturn cacheDir, nil\n\t}\n\n\t// Only check context if we need to clone (requires network)\n\tif err := ctx.Err(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"context cancelled while waiting for repository lock: %w\", err)\n\t}\n\n\tgetterURL := node.buildURL()\n\n\tclient := &getter.Client{\n\t\tCtx:  ctx,\n\t\tSrc:  getterURL,\n\t\tDst:  cacheDir,\n\t\tMode: getter.ClientModeDir,\n\t}\n\n\tif err := client.Get(); err != nil {\n\t\t_ = os.RemoveAll(cacheDir)\n\t\treturn \"\", fmt.Errorf(\"failed to clone repository: %w\", err)\n\t}\n\n\treturn cacheDir, nil\n}","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/go-task/task/blob/385e5ad92af02877b6d7cf9dcc963b5ed916e70a/taskfile/node_git.go#L123-L159","documentation":"Before cloning a remote git Taskfile, getOrCloneRepo checks ctx.Err(). If the context is already cancelled/deadlined while (about to be) waiting to acquire/clone the repository, the context error is wrapped with this message.","triggerScenarios":"ReadContext -> getOrCloneRepo with a ctx that is cancelled or has passed its deadline before the clone begins — e.g. `task` invocation with a timeout that expired, or Ctrl-C while resolving a remote `https://...git` include.","commonSituations":"Slow network plus a short context deadline; user interrupt during remote Taskfile resolution; orchestrators (CI job timeouts) cancelling the parent context before the clone starts.","solutions":["Re-run the command; this is usually a cancellation, not a data problem","Increase any deadline/timeout applied to the context wrapping Task","Pre-cache the repo by fetching the include manually or warming the cache dir","Check network/proxy speed if timeouts fire routinely before cloning completes"],"exampleFix":"// before\nctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)","handlingStrategy":"retry","validationCode":"select {\ncase <-ctx.Done():\n    return ctx.Err() // caller already knows context is dead; resolve before invoking task\ndefault:\n}\nif _, err := net.LookupHost(gitHost); err != nil {\n    return fmt.Errorf(\"git host unreachable: %w\", err)\n}","typeGuard":"func contextAlive(ctx context.Context) bool {\n    return ctx.Err() == nil\n}","tryCatchPattern":"data, err := node.ReadContext(ctx)\nif err != nil {\n    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n        // retry with a fresh, longer-lived context\n    }\n    return err\n}","preventionTips":["Give remote Taskfile resolution a generous timeout","Avoid Ctrl-C during first-run cache warm-up of remote includes","Pre-clone remote Taskfile repos to avoid network time entirely","Ensure parent contexts (CI job timeouts) outlive task execution"],"tags":["git","context-cancelled","network","remote-taskfile"],"backgroundTag":"context-cancelled","analyzedSha":"385e5ad92af02877b6d7cf9dcc963b5ed916e70a","analyzedAt":"2026-09-05T09:01:05.226Z","contentChangedAt":"2026-09-05T09:01:05.226Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}