{"record":{"id":"bf3c14cefb951ff9","repo":"kovidgoyal/kitty","slug":"cannot-read-from-s-with-error-w","errorCode":null,"errorMessage":"Cannot read from: %s with error: %w","messagePattern":"Cannot read from: (.+?) with error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kittens/ssh/config.go","lineNumber":194,"sourceCode":"\tif !filepath.IsAbs(ans) {\n\t\tans = paths_ctx.AbspathFromHome(ans)\n\t}\n\tif is_glob {\n\t\tfiles, err := doublestar.FilepathGlob(ans)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s is not a valid glob pattern with error: %w\", spec, err)\n\t\t}\n\t\tif len(files) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"%s matches no files\", spec)\n\t\t}\n\t\treturn files, nil\n\t}\n\terr := unix.Access(ans, unix.R_OK)\n\tif err != nil {\n\t\tif errors.Is(err, os.ErrNotExist) {\n\t\t\treturn nil, fmt.Errorf(\"%s does not exist\", spec)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"Cannot read from: %s with error: %w\", spec, err)\n\t}\n\treturn []string{ans}, nil\n}\n\nfunc get_arcname(loc, dest, home string) (arcname string) {\n\tif dest != \"\" {\n\t\tarcname = dest\n\t} else {\n\t\tarcname = filepath.Clean(loc)\n\t\tif strings.HasPrefix(arcname, home) {\n\t\t\tra, err := filepath.Rel(home, arcname)\n\t\t\tif err == nil {\n\t\t\t\tarcname = ra\n\t\t\t}\n\t\t}\n\t}\n\tprefix := \"home/\"\n\tif strings.HasPrefix(arcname, \"/\") {","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/kovidgoyal/kitty/blob/6d5d0c440603ad9bdf6dcd599f73f6dde21acb44/kittens/ssh/config.go#L176-L212","documentation":"Thrown by resolve_file_spec when unix.Access(path, R_OK) fails for a local file/directory spec in the ssh kitten's copy syntax. It means the path exists (or at least the error is not ENOENT) but the process cannot read it, typically EACCES/EPERM. The offending spec string is included in the message.","triggerScenarios":"ParseCopyInstruction with --copy or COPY: directives referencing a path that exists but is not readable by the current user (wrong ownership, missing read bit, or a parent directory without +x). Also paths with permission-restricted parents on some filesystems.","commonSituations":"Running kitty ssh kitten as a different user via sudo/su; files created by root in the home directory; hardened umask; NFS mounts with root_squash; passing /proc or /sys pseudo-files.","solutions":["Check the path's permissions: ls -l <spec> and chmod u+r (or chown) it","Verify every parent directory grants execute (x) permission to your user: namei -l <spec>","Run the kitten as the user who owns the file, or copy the file to a readable location first","If the path should not exist at all, remove it — a different error (does not exist) only fires on ENOENT"],"exampleFix":"# before\nkitty +kitten ssh --copy myuser@host:/data/secret.conf\n# after (make it readable)\nchmod u+r /data/secret.conf\nkitty +kitten ssh --copy myuser@host:/data/secret.conf","handlingStrategy":"validation","validationCode":"if err := unix.Access(path, unix.R_OK); err != nil {\n    log.Printf(\"cannot read %s: %v\", path, err)\n    // fix perms or skip\n}","typeGuard":null,"tryCatchPattern":"if _, err := kitten.ParseCopyInstruction(spec); err != nil {\n    if strings.Contains(err.Error(), \"Cannot read from\") { /* fix perms, skip file */ }\n}","preventionTips":["Pre-validate every path with os.Stat + unix.Access before building copy specs","Run the kitten as the file's owning user","Avoid copying root-owned paths via sudo-launched sessions"],"tags":["ssh-kitten","filesystem","permissions","file-access"],"backgroundTag":"permission-denied-file-read","analyzedSha":"6d5d0c440603ad9bdf6dcd599f73f6dde21acb44","analyzedAt":"2026-08-27T14:20:20.142Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}