{"record":{"id":"899215d44fc2d6d0","repo":"golang/go","slug":"import-cycle-not-allowed","errorCode":null,"errorMessage":"import cycle not allowed","messagePattern":"import cycle not allowed","errorType":"validation","errorClass":"PackageError","httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/load/pkg.go","lineNumber":1446,"sourceCode":"\t\tif !f.IsDir() && strings.HasSuffix(f.Name(), \".go\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// reusePackage reuses package p to satisfy the import at the top\n// of the import stack stk. If this use causes an import loop,\n// reusePackage updates p's error information to record the loop.\nfunc reusePackage(p *Package, stk *ImportStack) *Package {\n\t// We use p.Internal.Imports==nil to detect a package that\n\t// is in the midst of its own loadPackage call\n\t// (all the recursion below happens before p.Internal.Imports gets set).\n\tif p.Internal.Imports == nil {\n\t\tif p.Error == nil {\n\t\t\tp.Error = &PackageError{\n\t\t\t\tImportStack:   stk.Copy(),\n\t\t\t\tErr:           errors.New(\"import cycle not allowed\"),\n\t\t\t\tIsImportCycle: true,\n\t\t\t}\n\t\t} else if !p.Error.IsImportCycle {\n\t\t\t// If the error is already set, but it does not indicate that\n\t\t\t// we are in an import cycle, set IsImportCycle so that we don't\n\t\t\t// end up stuck in a loop down the road.\n\t\t\tp.Error.IsImportCycle = true\n\t\t}\n\t\tp.Incomplete = true\n\t}\n\t// Don't rewrite the import stack in the error if we have an import cycle.\n\t// If we do, we'll lose the path that describes the cycle.\n\tif p.Error != nil && p.Error.ImportStack != nil &&\n\t\t!p.Error.IsImportCycle && stk.shorterThan(p.Error.ImportStack.Pkgs()) {\n\t\tp.Error.ImportStack = stk.Copy()\n\t}\n\treturn p\n}","sourceCodeStart":1428,"sourceCodeEnd":1464,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/load/pkg.go#L1428-L1464","documentation":"Go forbids circular imports between packages. reusePackage detects a package that is in the middle of its own loadPackage recursion (its Internal.Imports slice is still nil because it is only set after the recursion completes), which can only happen if the import stack has looped back to a package currently being loaded. The package is marked Incomplete and the recorded ImportStack shows the cycle path.","triggerScenarios":"Package A imports B and B imports A (directly or transitively); during the depth-first load, reusePackage is invoked on package A while A.Internal.Imports is still nil because A's own loadPackage call has not yet returned.","commonSituations":"Splitting a package into two that both need each other's symbols; adding an import that closes a loop; refactoring that moves shared types so a dependency points back to the originator; test helper package imported by production code that itself imports the test target.","solutions":["Break the cycle by extracting the shared code into a third package that both packages import.","Invert the dependency: move the depending code into the package it currently depends on.","Decouple at compile time with an interface defined in a lower-level package and implemented where needed.","Run `go list -deps` or `go vet` to confirm the cycle is gone after the refactor."],"exampleFix":"// before: pkg a imports b, pkg b imports a -> cycle\n// after: move shared types into a/new shared package c\n//   a -> c, b -> c   (no edge between a and b)","handlingStrategy":"validation","validationCode":"// Run before pushing: `go build ./...` fails fast on cycles.\n// Programmatically, walk the import graph and assert acyclicity:\n//   out, err := exec.Command(\"go\", \"list\", \"-deps\", \"./...\").Output()\n// then check `go list -deps -f '{{.ImportPath}} {{.Imports}}' ./...` for back-edges.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep packages small and focused on one responsibility to reduce cross-package coupling.","Avoid bidirectional dependencies; if A and B reference each other, extract a lower package C.","Run `go build ./...` or `go vet` frequently so cycles surface at the introducing commit."],"tags":["go","imports","build","cycle"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}