golang/go · error

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

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.

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)

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.