{"record":{"id":"8549301c377419a8","repo":"nektos/act","slug":"copy-cancelled","errorCode":null,"errorMessage":"copy cancelled","messagePattern":"copy cancelled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/filecollector/file_collector.go","lineNumber":137,"sourceCode":"func (*DefaultFs) Open(path string) (io.ReadCloser, error) {\n\treturn os.Open(path)\n}\n\nfunc (*DefaultFs) Readlink(path string) (string, error) {\n\treturn os.Readlink(path)\n}\n\n//nolint:gocyclo\nfunc (fc *FileCollector) CollectFiles(ctx context.Context, submodulePath []string) filepath.WalkFunc {\n\ti, _ := fc.Fs.OpenGitIndex(path.Join(fc.SrcPath, path.Join(submodulePath...)))\n\treturn func(file string, fi os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ctx != nil {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn fmt.Errorf(\"copy cancelled\")\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\n\t\tsansPrefix := strings.TrimPrefix(file, fc.SrcPrefix)\n\t\tsplit := strings.Split(sansPrefix, string(filepath.Separator))\n\t\t// The root folders should be skipped, submodules only have the last path component set to \".\" by filepath.Walk\n\t\tif fi.IsDir() && len(split) > 0 && split[len(split)-1] == \".\" {\n\t\t\treturn nil\n\t\t}\n\t\tvar entry *index.Entry\n\t\tif i != nil {\n\t\t\tentry, err = i.Entry(strings.Join(split[len(submodulePath):], \"/\"))\n\t\t} else {\n\t\t\terr = index.ErrEntryNotFound\n\t\t}\n\t\tif err != nil && fc.Ignorer != nil && fc.Ignorer.Match(split, fi.IsDir()) {\n\t\t\tif fi.IsDir() {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/filecollector/file_collector.go#L119-L155","documentation":"Returned by the FileCollector's WalkFunc when the context passed to CollectFiles has been cancelled (ctx.Done() fires) mid-walk. It is a cooperative-cancellation error: the artifact/cache file copy loop checks the context before each visited file and aborts the traversal.","triggerScenarios":"A job-level or global context timeout/interrupt (SIGINT, workflow cancellation, parent executor abort) fires while act is copying workspace files or preparing an upload, so the very next file visit returns this error.","commonSituations":"User hits Ctrl-C during artifact upload or workspace tar preparation; a job timeout cancels the context during cache save; test harnesses cancel the run context while file collection is in progress.","solutions":["If the cancellation was accidental (aggressive timeout), raise the timeout and re-run.","Treat this error as expected on interrupt: compare with errors.Is(ctx.Err(), context.Canceled) and skip logging it as a failure.","Reduce the size of the copied tree (add .actignore / exclude paths) so collection finishes before the deadline."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"select {\ncase <-ctx.Done():\n    return ctx.Err() // skip file collection entirely when already cancelled\ndefault:\n}\n_ = collector.CollectFiles(ctx, nil)","typeGuard":null,"tryCatchPattern":"err := exec(ctx)\nif err != nil {\n    if errors.Is(err, context.Canceled) || errors.Is(ctx.Err(), context.Canceled) || err.Error() == \"copy cancelled\" {\n        return nil // treat interrupt as clean stop, not a failure\n    }\n    return err\n}","preventionTips":["Treat 'copy cancelled' as expected during SIGINT and suppress noisy failure logs.","Size timeouts to the workspace size; large repos need longer collection windows.","Exclude heavyweight directories from the workspace copy."],"tags":["cancellation","context","filecollector","artifacts"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}