{"record":{"id":"decad37332e9190b","repo":"larksuite/cli","slug":"s-w-decad3","errorCode":null,"errorMessage":"%s: %w","messagePattern":"%s: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"shortcuts/common/runner.go","lineNumber":670,"sourceCode":"\t}\n\tresolved, err := fio.ResolvePath(path)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"resolve save path: %w\", err)\n\t}\n\tif resolved == \"\" {\n\t\treturn \"\", fmt.Errorf(\"resolve save path: empty result for %q\", path)\n\t}\n\treturn resolved, nil\n}\n\n// WrapOpenError matches a FileIO.Open/Stat error and wraps it with the\n// caller-provided message prefix.\nfunc WrapOpenError(err error, pathMsg, readMsg string) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif errors.Is(err, fileio.ErrPathValidation) {\n\t\treturn fmt.Errorf(\"%s: %w\", pathMsg, err)\n\t}\n\treturn fmt.Errorf(\"%s: %w\", readMsg, err)\n}\n\n// WrapInputStatErrorTyped wraps a FileIO.Stat/Open error for input file\n// validation, returning a typed validation error with the appropriate message:\n//   - Path validation failures → \"unsafe file path: ...\"\n//   - Other errors → readMsg prefix (default \"cannot read file\")\n//\n// Pass an optional readMsg to override the non-path-validation message prefix.\nfunc WrapInputStatErrorTyped(err error, readMsg ...string) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif errors.Is(err, fileio.ErrPathValidation) {\n\t\treturn errs.NewValidationError(errs.SubtypeInvalidArgument, \"unsafe file path: %s\", err).\n\t\t\tWithCause(err)\n\t}","sourceCodeStart":652,"sourceCodeEnd":688,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/common/runner.go#L652-L688","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check the wrapped cause: errors.Is(err, fileio.ErrPathValidation) distinguishes unsafe paths from read failures","For unsafe paths, pass a workspace-relative path without '..' segments or symlinks escaping the workdir","For read failures, verify the file exists and is readable before invoking the command"],"exampleFix":"// before\nerr := common.WrapOpenError(err, \"invalid --input path\", \"cannot read --input file\")\n// after (guard at call site)\nif err := ctx.ValidatePath(input); err != nil { return err } // fails early with clear message","handlingStrategy":"validation","validationCode":"if err := ctx.ValidatePath(inputPath); err != nil { return err } // fails early before Open","typeGuard":"func isUnsafePathErr(err error) bool { return errors.Is(err, fileio.ErrPathValidation) }","tryCatchPattern":"if err != nil { wrapped := common.WrapOpenError(err, \"invalid path\", \"cannot read file\"); if errors.Is(wrapped, fileio.ErrPathValidation) { /* advise relative path */ } return wrapped }","preventionTips":["Always pass workspace-relative input paths","Validate with ctx.ValidatePath before opening","Never embed '..' or absolute paths in input flags"],"tags":["fileio","path-validation","error-wrapping"],"backgroundTag":"unsafe-file-path","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}