opentofu/opentofu · error

invalid root module source address: %w

Error message

invalid root module source address: %w

What it means

The new-runtime scaffolding converts the root module directory into a module source address by prefixing './' for relative paths and calling addrs.ParseModuleSource, which was designed for module block source arguments, not arbitrary filesystem paths (the comment in the code says exactly this). The error wraps a parse failure: the directory path contains characters that cannot be interpreted as a local source address, such as ':' (which makes the path look like a registry/remote address, including Windows drive letters), '?' or '#'.

Source

Thrown at internal/command/temp_new_runtime.go:107

		Provisioners:       staticPlugins,
		Workspace:          workspace,
	}

	// The new config-loading system wants to work in terms of module source
	// addresses rather than raw local filenames, so we'll ask the
	// addrs package to parse the path we were given. We need to adjust
	// a little though, because this function was designed for parsing
	// the "source" argument in a module block, not a plain filepath.
	// We should add a function in package addrs that's actually intended for
	// turning arbitrary filesystem paths in to addrs.LocalSource in the long
	// run, but this will do for now.
	configDir := root.SourceDir
	if !filepath.IsAbs(configDir) {
		configDir = "." + string(filepath.Separator) + configDir
	}
	rootModuleSource, err := addrs.ParseModuleSource(configDir)
	if err != nil {
		diags = diags.Append(fmt.Errorf("invalid root module source address: %w", err))
		return nil, diags
	}

	configCall := &eval.ConfigCall{
		RootModuleSource:     rootModuleSource,
		InputValues:          inputValues,
		AllowImpureFunctions: false,
		EvalContext:          evalCtx,
	}
	configInst, moreDiags := eval.NewConfigInstance(ctx, configCall)
	diags = diags.Append(moreDiags)
	if moreDiags.HasErrors() {
		return nil, diags
	}
	return configInst, diags
}

// newRuntimeModules is an implementation of [eval.ExternalModules] that makes

View on GitHub (pinned to 3561785c48)

Solutions

  1. Run the command from a path without ':', '?', '#' characters — on Windows, try invoking from a relative path so the './' prefix branch is used
  2. Check the -chdir argument and current directory for stray address-like characters
  3. Move or symlink the checkout into a plain path (e.g. C:\src\mod or ~/src/mod) and retry
  4. If it fails on a clean POSIX path, report it — the code itself flags this path-to-address conversion as a stopgap

Example fix

# before (Windows absolute path is parsed as a remote address)
tofu test -chdir C:\proj\mod

# after
cd C:\proj\mod && tofu test
Defensive patterns

Strategy: validation

Validate before calling

abs, err := filepath.Abs(configDir)
if err != nil {
	return err
}
for _, bad := range []string{":", "?", "#"} {
	if strings.Contains(abs, bad) {
		// ParseModuleSource would reject this as a non-local address
		return fmt.Errorf("path %q contains %q and cannot be used as a local module source", abs, bad)
	}
}

Try / catch

rootModuleSource, err := addrs.ParseModuleSource(configDir)
if err != nil {
	// recover by retrying with a cleaned relative path from the module root
	if rel, relErr := filepath.Rel(moduleRoot, configDir); relErr == nil {
		rootModuleSource, err = addrs.ParseModuleSource("." + string(filepath.Separator) + rel)
	}
	if err != nil {
		diags = diags.Append(fmt.Errorf("invalid root module source address: %w", err))
	}
}

Prevention

When it happens

Trigger: Absolute Windows paths (C:\... do not start with ./ so they are parsed as remote addresses and rejected); directory names containing ':', '?', '#', or other address syntax; a root.SourceDir that is empty or degenerate after -chdir handling.

Common situations: Running the new-runtime-based commands (tofu test and similar) on Windows from drive-letter paths; checkouts under directories with unusual characters; odd -chdir arguments.

Related errors


AI-assisted analysis of opentofu/opentofu@3561785c48 (2026-08-15). Data as JSON: /api/errors/95dedfafe6b456d8. Report an issue: GitHub.