golang/go · error
Config.Importer.ImportFrom
Error message
Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error
What it means
When the type checker's Config.Importer implements the ImporterFrom interface, it calls ImportFrom(path, dir, 0). If the importer returns (nil, nil) — a nil package with no error — this violates the importer contract. A nil package must always be accompanied by an error explaining why the import failed. This error is synthesized by the type checker to flag the contract violation.
Solutions
- Fix the custom importer to always return a non-nil error when the package is nil
- Use importer.Default() or go/packages instead of a custom implementation
- Add a defensive guard in the importer: if pkg == nil && err == nil { err = fmt.Errorf("import %q failed", path) }
- Write unit tests for the custom importer that verify it never returns (nil, nil)
Example fix
// before — buggy custom importer
func (m *MyImporter) ImportFrom(path, dir string, mode types.ImportMode) (*types.Package, error) {
pkg := m.cache[path]
if pkg == nil {
return nil, nil // BUG: contract violation
}
return pkg, nil
}
// after — fixed
func (m *MyImporter) ImportFrom(path, dir string, mode types.ImportMode) (*types.Package, error) {
pkg := m.cache[path]
if pkg == nil {
return nil, fmt.Errorf("package %q not found in cache", path)
}
return pkg, nil
} Defensive patterns
Strategy: validation
Validate before calling
// Wrap a custom importer to enforce the (nil package => non-nil error) contract
type safeImporter struct {
inner types.Importer
}
func (s *safeImporter) Import(path string) (*types.Package, error) {
pkg, err := s.inner.Import(path)
if pkg == nil && err == nil {
return nil, fmt.Errorf("importer returned nil package for %q without error", path)
}
return pkg, err
}
func (s *safeImporter) ImportFrom(path, dir string, mode types.ImportMode) (*types.Package, error) {
if imp, ok := s.inner.(types.ImporterFrom); ok {
pkg, err := imp.ImportFrom(path, dir, mode)
if pkg == nil && err == nil {
return nil, fmt.Errorf("ImportFrom returned nil package for %q without error", path)
}
return pkg, err
}
return s.Import(path)
}
// Usage: conf.Importer = &safeImporter{inner: myCustomImporter} Type guard
// Verify a custom importer satisfies the contract in tests
func assertImporterContract(imp types.Importer) error {
// Test with a path that should not resolve
_, err := imp.Import("github.com/nonexistent/pkg/that/does/not/exist")
// If err is nil and we get here, check if we got a nil package
// (the real test is that Import never returns (nil, nil))
if err == nil {
// If no error for a nonexistent package, the importer is too permissive
// but that's not a contract violation per se
}
return nil
} Prevention
- Always return a non-nil error from custom Importer implementations when the package is nil
- Wrap custom importers with a safeImporter guard that enforces the contract
- Write unit tests that call Import with non-existent paths and verify an error is returned
- Prefer using importer.Default() or go/packages over custom importer implementations
When it happens
Trigger: A custom types.Importer implementation whose ImportFrom method returns (nil, nil) for a not-found or error case instead of returning a proper error. Incomplete or buggy mock importer implementations in tests. Importers that swallow errors internally and return nil.
Common situations: Custom importer implementations in static analysis tools that have bugs in error handling. Mock importers in unit tests that don't properly simulate the error contract. Importers that check a cache and return nil on miss without falling through to actual resolution.
Related errors
- Config.Importer not installed
- empty string
- invalid character %#U
- cannot instantiate non-generic
- cannot instantiate : got type arguments but have type…
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/5f1b25a960094e65.
Report an issue: GitHub.
Appendix: source
Thrown at src/cmd/compile/internal/types2/resolver.go:153
}
// no package yet => import it
if path == "C" && (check.conf.FakeImportC || check.conf.go115UsesCgo) {
if check.conf.FakeImportC && check.conf.go115UsesCgo {
check.error(pos, BadImportPath, "cannot use FakeImportC and go115UsesCgo together")
}
imp = NewPackage("C", "C")
imp.fake = true // package scope is not populated
imp.cgo = check.conf.go115UsesCgo
} else {
// ordinary import
var err error
if importer := check.conf.Importer; importer == nil {
err = fmt.Errorf("Config.Importer not installed")
} else if importerFrom, ok := importer.(ImporterFrom); ok {
imp, err = importerFrom.ImportFrom(path, dir, 0)
if imp == nil && err == nil {
err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, dir)
}
} else {
imp, err = importer.Import(path)
if imp == nil && err == nil {
err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
}
}
// make sure we have a valid package name
// (errors here can only happen through manipulation of packages after creation)
if err == nil && imp != nil && (imp.name == "_" || imp.name == "") {
err = fmt.Errorf("invalid package name: %q", imp.name)
imp = nil // create fake package below
}
if err != nil {
check.errorf(pos, BrokenImport, "could not import %s (%s)", path, err)
if imp == nil {
// create a new fake package
// come up with a sensible package name (heuristic)View on GitHub (pinned to b6b368adc5)