{"record":{"id":"7c2e952b3640e78e","repo":"golang/go","slug":"import-cycle-not-allowed-in-test","errorCode":null,"errorMessage":"import cycle not allowed in test","messagePattern":"import cycle not allowed in test","errorType":"validation","errorClass":"PackageError","httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/load/test.go","lineNumber":570,"sourceCode":"\t\t\t\t}\n\t\t\t\tstk = append(stk, ImportInfo{\n\t\t\t\t\tPkg: p.ImportPath,\n\t\t\t\t\tPos: extractFirstImport(importer.Internal.Build.ImportPos[p.ImportPath]),\n\t\t\t\t})\n\t\t\t\tp = importerOf[p]\n\t\t\t}\n\t\t\t// complete the cycle: we set importer[p] = nil to break the cycle\n\t\t\t// in importerOf, it's an implicit importerOf[p] == pTest. Add it\n\t\t\t// back here since we reached nil in the loop above to demonstrate\n\t\t\t// the cycle as (for example) package p imports package q imports package r\n\t\t\t// imports package p.\n\t\t\tstk = append(stk, ImportInfo{\n\t\t\t\tPkg: ptest.ImportPath,\n\t\t\t})\n\t\t\tslices.Reverse(stk)\n\t\t\treturn &PackageError{\n\t\t\t\tImportStack:   stk,\n\t\t\t\tErr:           errors.New(\"import cycle not allowed in test\"),\n\t\t\t\tIsImportCycle: true,\n\t\t\t}\n\t\t}\n\t\tfor _, dep := range p.Internal.Imports {\n\t\t\tif _, ok := importerOf[dep]; !ok {\n\t\t\t\timporterOf[dep] = p\n\t\t\t\tq = append(q, dep)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// isTestFunc tells whether fn has the type of a testing function. arg\n// specifies the parameter type we look for: B, F, M or T.\nfunc isTestFunc(fn *ast.FuncDecl, arg string) bool {\n\tif fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||","sourceCodeStart":552,"sourceCodeEnd":588,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/load/test.go#L552-L588","documentation":"External test packages (foo_test) may import the package under test and its dependencies; the test loader synthesizes a combined graph. A BFS from the test package detects when the synthesized test variant participates in an import loop and records the demonstrated cycle (e.g. p -> q -> r -> p).","triggerScenarios":"The test package p_test imports a package whose dependency graph, when test variants are merged, leads back to p or p_test; importerOf BFS returns a PackageError with IsImportCycle set.","commonSituations":"Test helpers placed in a package that imports the package under test, while production code was later made to depend on those helpers; an external test package that imports another package's external test helper; cycles only visible once _test variants are included.","solutions":["Move shared test helpers into an internal non-test package imported by both the package and its tests.","Remove the production dependency that closes the loop.","Use an interface defined in a lower-level package to break the compile-time cycle."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Run `go vet ./...` and `go test -run xxx ./...` (compile only) to surface\n// test-only cycles before the full test run. `go list -test -deps ./...`\n// exposes the synthesized test import graph for inspection.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Put shared test helpers in an internal package, not in a _test.go file that another package must import.","Avoid making production packages depend on packages used only by tests."],"tags":["go","testing","imports","cycle","build"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}