golang/go · error

error loading go.work

Error message

error loading go.work:
%s:%d: path %s appears multiple times in workspace

What it means

Loading a go.work file found the same module root path listed in two or more 'use' directives. The loader de-duplicates by absolute modRoot (relative paths are joined to workDir first), and the second occurrence is rejected. Line number of the offending 'use' directive is included.

Solutions

  1. Edit go.work and remove the duplicate 'use' line at the reported line number.
  2. Run 'go work edit -dropuse=./duppath go.work' to drop one entry.
  3. Rebuild the file with 'go work use .' letting the tool regenerate a clean, deduplicated go.work.
  4. Verify the two paths really resolve to the same directory (absolutize them) before deleting.

Example fix

// before (go.work)
go 1.22
use (
    ./svc-a
    ./svc-a      // line 4: duplicate
)
// error: path .../svc-a appears multiple times in workspace

// after
go 1.22
use (
    ./svc-a
)
Defensive patterns

Strategy: validation

Validate before calling

// Deduplicate go.work 'use' entries by absolute path before relying on it.
data, _ := os.ReadFile("go.work")
wf, _ := modfile.ParseWork("go.work", data, nil)
seen := map[string]bool{}
for _, u := range wf.Use {
    abs, _ := filepath.Abs(u.Path)
    if seen[abs] {
        return fmt.Errorf("duplicate use %s", u.Path)
    }
    seen[abs] = true
}

Try / catch

out, err := exec.Command("go", "build", "./...").CombinedOutput()
if err != nil && bytes.Contains(out, []byte("appears multiple times in workspace")) {
    // regenerate go.work cleanly
    if rerr := exec.Command("go", "work", "edit", "-fmt", "go.work").Run(); rerr != nil {
        return rerr
    }
    out, err = exec.Command("go", "build", "./...").CombinedOutput()
}
return err

Prevention

When it happens

Trigger: go.work contains duplicate 'use' entries, e.g. 'use ./a' and 'use ./a' or 'use ./a' plus 'use ../repo/a' that resolve to the same absolute path. Surfaces during any workspace-aware command.

Common situations: Hand-edited go.work; 'go work use ./a' run twice without dedup awareness (the tool normally dedups, but manual edits or merges reintroduce duplicates); path normalization differences between relative entries.

Related errors


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

Appendix: source

Thrown at src/cmd/go/internal/modload/init.go:819

// LoadWorkFile parses and checks the go.work file at the given path,
// and returns the absolute paths of the workspace modules' modroots.
// It does not modify the global state of the modload package.
func LoadWorkFile(path string) (workFile *modfile.WorkFile, modRoots []string, err error) {
	workDir := filepath.Dir(path)
	wf, err := ReadWorkFile(path)
	if err != nil {
		return nil, nil, err
	}
	seen := map[string]bool{}
	for _, d := range wf.Use {
		modRoot := d.Path
		if !filepath.IsAbs(modRoot) {
			modRoot = filepath.Join(workDir, modRoot)
		}

		if seen[modRoot] {
			return nil, nil, fmt.Errorf("error loading go.work:\n%s:%d: path %s appears multiple times in workspace", base.ShortPath(path), d.Syntax.Start.Line, modRoot)
		}
		seen[modRoot] = true
		modRoots = append(modRoots, modRoot)
	}

	for _, g := range wf.Godebug {
		if err := CheckGodebug("godebug", g.Key, g.Value); err != nil {
			return nil, nil, fmt.Errorf("error loading go.work:\n%s:%d: %w", base.ShortPath(path), g.Syntax.Start.Line, err)
		}
	}

	return wf, modRoots, nil
}

// ReadWorkFile reads and parses the go.work file at the given path.
func ReadWorkFile(path string) (*modfile.WorkFile, error) {
	path = base.ShortPath(path) // use short path in any errors
	workData, err := fsys.ReadFile(path)

View on GitHub (pinned to b6b368adc5)