larksuite/cli · warning

%s: %w

Error message

%s: %w

What it means

WrapOpenError wraps a FileIO.Open/Stat error with a caller-provided message prefix. If the underlying error matches fileio.ErrPathValidation (unsafe path: absolute, traversal, symlink escape), it uses pathMsg; otherwise it uses readMsg (typically 'cannot read file'). The original error is preserved via %w so errors.Is/As still work.

Source

Thrown at shortcuts/common/runner.go:670

	}
	resolved, err := fio.ResolvePath(path)
	if err != nil {
		return "", fmt.Errorf("resolve save path: %w", err)
	}
	if resolved == "" {
		return "", fmt.Errorf("resolve save path: empty result for %q", path)
	}
	return resolved, nil
}

// WrapOpenError matches a FileIO.Open/Stat error and wraps it with the
// caller-provided message prefix.
func WrapOpenError(err error, pathMsg, readMsg string) error {
	if err == nil {
		return nil
	}
	if errors.Is(err, fileio.ErrPathValidation) {
		return fmt.Errorf("%s: %w", pathMsg, err)
	}
	return fmt.Errorf("%s: %w", readMsg, err)
}

// WrapInputStatErrorTyped wraps a FileIO.Stat/Open error for input file
// validation, returning a typed validation error with the appropriate message:
//   - Path validation failures → "unsafe file path: ..."
//   - Other errors → readMsg prefix (default "cannot read file")
//
// Pass an optional readMsg to override the non-path-validation message prefix.
func WrapInputStatErrorTyped(err error, readMsg ...string) error {
	if err == nil {
		return nil
	}
	if errors.Is(err, fileio.ErrPathValidation) {
		return errs.NewValidationError(errs.SubtypeInvalidArgument, "unsafe file path: %s", err).
			WithCause(err)
	}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Check the wrapped cause: errors.Is(err, fileio.ErrPathValidation) distinguishes unsafe paths from read failures
  2. For unsafe paths, pass a workspace-relative path without '..' segments or symlinks escaping the workdir
  3. For read failures, verify the file exists and is readable before invoking the command

Example fix

// before
err := common.WrapOpenError(err, "invalid --input path", "cannot read --input file")
// after (guard at call site)
if err := ctx.ValidatePath(input); err != nil { return err } // fails early with clear message
Defensive patterns

Strategy: validation

Validate before calling

if err := ctx.ValidatePath(inputPath); err != nil { return err } // fails early before Open

Type guard

func isUnsafePathErr(err error) bool { return errors.Is(err, fileio.ErrPathValidation) }

Try / catch

if err != nil { wrapped := common.WrapOpenError(err, "invalid path", "cannot read file"); if errors.Is(wrapped, fileio.ErrPathValidation) { /* advise relative path */ } return wrapped }

Prevention

When it happens

Trigger: Any shortcut call site that validates an input file and passes the resulting error to common.WrapOpenError: path rejected by SafeInputPath validation (pathMsg branch), or Open/Stat failure such as missing file or permission denied (readMsg branch).

Common situations: User passes --file with '../etc/passwd' or an absolute path (path validation); user passes a nonexistent or unreadable file; CI environment lacks the workspace file.

Related errors


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