larksuite/cli · error

%s: %w

Error message

%s: %w

What it means

SafeLocalFlagPath validates a flag value as a local file path by delegating to SafeInputPath; on failure it prefixes the error with the flag name. Empty values and http/https URLs bypass validation entirely, so this fires only for non-URL local path values that fail the strict path policy.

Source

Thrown at internal/vfs/localfileio/path.go:117

			return err
		}
	}
	return nil
}

func isWindowsNonLocalNamespace(path string) bool {
	normalized := strings.ReplaceAll(path, "/", `\`)
	return strings.HasPrefix(normalized, `\\`) || strings.HasPrefix(normalized, `\??\`)
}

// SafeLocalFlagPath validates a flag value as a local file path.
// Empty values and http/https URLs are returned unchanged without validation.
func SafeLocalFlagPath(flagName, value string) (string, error) {
	if value == "" || strings.HasPrefix(value, "http://") || strings.HasPrefix(value, "https://") {
		return value, nil
	}
	if _, err := SafeInputPath(value); err != nil {
		return "", fmt.Errorf("%s: %w", flagName, err)
	}
	return value, nil
}

// SafeEnvDirPath validates an environment-provided application directory path.
// It requires an absolute path, rejects control characters, normalizes the
// input, and resolves symlinks through the nearest existing ancestor.
func SafeEnvDirPath(path, envName string) (string, error) {
	if err := charcheck.RejectControlChars(path, envName); err != nil {
		return "", err
	}

	path = filepath.Clean(path)
	if !filepath.IsAbs(path) {
		return "", fmt.Errorf("%s must be an absolute path, got %q", envName, path)
	}

	resolved, err := resolveNearestAncestor(path)

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Read the wrapped %w cause to see which policy check failed and move the file accordingly (cwd, /tmp, or ~/files)
  2. If the value is a URL, use an http:// or https:// URL (those bypass path validation)
  3. Use a relative path that stays inside the current working directory
  4. If the file must come from anywhere, pass it via stdin ("-") instead of a path

Example fix

// before
lark cmd --attachment /etc/hosts
// error: --attachment: --file "/etc/hosts" resolves outside the allowed roots
// after
cp /etc/hosts /tmp/hosts.txt && lark cmd --attachment /tmp/hosts.txt
Defensive patterns

Strategy: validation

Validate before calling

if v != "" && !strings.HasPrefix(v, "http://") && !strings.HasPrefix(v, "https://") {
    if _, err := os.Stat(v); err != nil {
        return fmt.Errorf("%s: local file %q unavailable: %w", flag, v, err)
    }
}

Type guard

func isHTTPURL(v string) bool {
    return strings.HasPrefix(v, "http://") || strings.HasPrefix(v, "https://")
}

Try / catch

if _, err := localfileio.SafeLocalFlagPath("--file", v); err != nil {
    var pathErr *os.PathError
    if errors.As(err, &pathErr) { /* fix path */ }
    return fmt.Errorf("%w (allowed roots: cwd, /tmp, ~/files)", err)
}

Prevention

When it happens

Trigger: Calling SafeLocalFlagPath(flagName, value) where value is a non-empty local (non-http/https) string that SafeInputPath rejects — outside the allowlist, containing control characters, a foreign absolute path like C:\x on Unix, a ~/ literal reading outside the policy, etc.

Common situations: Passing a file:// or other scheme URL that isn't http/https; passing a Windows path on Linux; pointing at a file outside cwd, /tmp, or ~/files; a flag intended for a URL given a local path.

Related errors


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