larksuite/cli · error

path must not be drive-relative; give a full path or a path

Error message

path must not be drive-relative; give a full path or a path without a drive letter

What it means

On Windows, `validatePathPlatform` rejects drive-relative paths like `C:foo` — a path with a volume but no root slash. Such a path resolves against that drive's own per-drive current directory, so the validated location would differ from the one the OS opens; it would also slip past the NTFS alternate-data-stream colon check. The error is wrapped with the flag name by safePath.

Source

Thrown at internal/vfs/localfileio/path_local_windows.go:29

	"strings"
)

// validatePathPlatform rejects Windows path shapes the policy cannot reason
// about: network/device namespaces (UNC, \\?\) and NTFS alternate data
// streams (a colon anywhere past the drive letter would address a hidden
// stream on an otherwise-allowed file).
func validatePathPlatform(path string) error {
	if isWindowsNonLocalNamespace(path) {
		return fmt.Errorf("path must not use a Windows network or device namespace")
	}
	cleaned := filepath.Clean(path)
	// A drive-relative path ("C:foo") carries a volume but is not absolute: it
	// resolves against that drive's own current directory, so the location it
	// names is not the one this validation can see. It is also how the stream
	// check below would be slipped, since "C:" is stripped as the volume and
	// the remaining "foo" holds no colon.
	if filepath.VolumeName(cleaned) != "" && !filepath.IsAbs(cleaned) {
		return fmt.Errorf("path must not be drive-relative; give a full path or a path without a drive letter")
	}
	if strings.Contains(cleaned[len(filepath.VolumeName(cleaned)):], ":") {
		return fmt.Errorf("path must not address an NTFS alternate data stream")
	}
	return nil
}

func validateLocalInputPlatform(path string) error {
	if isWindowsNonLocalNamespace(path) {
		return fmt.Errorf("local input path must not use a Windows network or device namespace")
	}

	cleaned := filepath.Clean(path)
	volume := filepath.VolumeName(cleaned)
	remainder := strings.TrimLeft(cleaned[len(volume):], `\/`)
	for _, component := range strings.FieldsFunc(remainder, func(r rune) bool {
		return r == '\\' || r == '/'
	}) {

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Insert the root separator: change `C:foo` to `C:\foo`.
  2. Drop the drive letter and use a normal relative path resolved against the current directory.
  3. Rebuild the path programmatically with filepath.Join (or filepath.Abs) instead of string concatenation.

Example fix

// before
p := "C:" + "data\\out.bin"        // C:data\out.bin
// after
p := filepath.Join("C:\\", "data", "out.bin")  // C:\data\out.bin
Defensive patterns

Strategy: validation

Validate before calling

// Go: normalize drive-relative paths to full paths
if vol := filepath.VolumeName(p); vol != "" && !filepath.IsAbs(p) {
    p = vol + "\\" + strings.TrimPrefix(p, vol)
}

Prevention

When it happens

Trigger: Passing `--file C:report.pdf` or `--output D:data\out.bin` on Windows (drive letter immediately followed by a non-separator). Note `C:\abs\path` and plain `relative\path` are fine.

Common situations: Scripts that string-concatenate a drive letter with a path missing the separator (`drive + ':' + path`); paths captured from tools that report drive-relative locations; per-drive working directories left over from `cd D:` in cmd.exe.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/f55455161cd34abb. Report an issue: GitHub.