golang/go · error

cannot embed %s %s: in different module

Error message

cannot embed %s %s: in different module

What it means

Thrown while walking each ancestor directory of an embed match (loop at line 2204). For every directory from the matched file up toward pkgdir, the loader stats <dir>/go.mod; if a go.mod exists, the embed target is inside a different module. go:embed deliberately cannot cross module boundaries because the embedded bytes would not be reproducible from go.mod alone.

Source

Thrown at src/cmd/go/internal/load/pkg.go:2207

		var list []string
		for _, file := range match {
			// relative path to p.Dir which begins without prefix slash
			rel := filepath.ToSlash(str.TrimFilePathPrefix(file, pkgdir))

			what := "file"
			info, err := fsys.Lstat(file)
			if err != nil {
				return nil, nil, err
			}
			if info.IsDir() {
				what = "directory"
			}

			// Check that directories along path do not begin a new module
			// (do not contain a go.mod).
			for dir := file; len(dir) > len(pkgdir)+1 && !dirOK[dir]; dir = filepath.Dir(dir) {
				if _, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil {
					return nil, nil, fmt.Errorf("cannot embed %s %s: in different module", what, rel)
				}
				if dir != file {
					if info, err := fsys.Lstat(dir); err == nil && !info.IsDir() {
						return nil, nil, fmt.Errorf("cannot embed %s %s: in non-directory %s", what, rel, dir[len(pkgdir)+1:])
					}
				}
				dirOK[dir] = true
				if elem := filepath.Base(dir); isBadEmbedName(elem) {
					if dir == file {
						return nil, nil, fmt.Errorf("cannot embed %s %s: invalid name %s", what, rel, elem)
					} else {
						return nil, nil, fmt.Errorf("cannot embed %s %s: in invalid directory %s", what, rel, elem)
					}
				}
			}

			switch {
			default:

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Move the files you want to embed out of the nested module directory into the current package's tree so no go.mod sits between them.
  2. Delete the stray go.mod in the subdirectory if that subdirectory is not meant to be its own module.
  3. Restructure so the embedded assets live in the same module as the package with the //go:embed directive.
  4. If you really need the nested module's files, copy them at build time via go:generate rather than //go:embed.

Example fix

// before: //go:embed vendor/foolib/assets  (vendor/foolib/go.mod exists)
// after: move assets to ./assets in the main module, then
//go:embed assets
Defensive patterns

Strategy: validation

Validate before calling

// Ensure no go.mod sits between the package dir and any embed target.
package embedcheck

import (
	"errors"
	"os"
	"path/filepath"
)

func NoNestedModuleBetween(pkgDir, target string) error {
	abs, err := filepath.Abs(filepath.Join(pkgDir, target))
	if err != nil {
		return err
	}
	for d := abs; len(d) > len(pkgDir)+1; d = filepath.Dir(d) {
		if _, err := os.Stat(filepath.Join(d, "go.mod")); err == nil {
			return errors.New("embed target crosses module boundary at " + d)
		}
	}
	return nil
}

Prevention

When it happens

Trigger: Embedding a path that descends into a nested module: e.g. a vendored copy or a git submodule that has its own go.mod, or a workspace member nested under the main module directory. The match was produced by fsys.Glob, then the ancestor walk finds the foreign go.mod.

Common situations: Adding a nested git submodule that ships a go.mod; using a `replace` to a local path that itself is a module and then trying to embed its files from the parent; embedding a directory that contains a checked-out dependency.

Related errors


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