golang/go · error

Config.Importer.ImportFrom(%s, %s, 0) returned nil but no er

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.

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)

Solutions

  1. Fix the custom importer to always return a non-nil error when the package is nil
  2. Use importer.Default() or go/packages instead of a custom implementation
  3. Add a defensive guard in the importer: if pkg == nil && err == nil { err = fmt.Errorf("import %q failed", path) }
  4. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/5f1b25a960094e65. Report an issue: GitHub.