{"record":{"id":"6296e75f0d56e93f","repo":"larksuite/cli","slug":"s-path-q-is-a-directory-not-a-file","errorCode":null,"errorMessage":"%s: path %q is a directory, not a file","messagePattern":"(.+?): path %q is a directory, not a file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/binding/audit.go","lineNumber":88,"sourceCode":"// orthogonal concern and the audit is intentionally Go-stdlib strict here.\n// Callers that accept user-authored config (e.g. resolveFileRef) must\n// pre-resolve any such shortcuts before passing the path in.\nfunc requireAbsolutePath(target, label string) error {\n\tif !filepath.IsAbs(target) {\n\t\treturn fmt.Errorf(\"%s: path must be absolute, got %q\", label, target)\n\t}\n\treturn nil\n}\n\n// lstatNonDir stats the path without following symlinks, rejecting\n// directories. Returns the stat info for downstream steps to reuse.\nfunc lstatNonDir(target, label string) (fs.FileInfo, error) {\n\tinfo, err := vfs.Lstat(target)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: cannot stat %q: %w\", label, target, err)\n\t}\n\tif info.IsDir() {\n\t\treturn nil, fmt.Errorf(\"%s: path %q is a directory, not a file\", label, target)\n\t}\n\treturn info, nil\n}\n\n// resolveSymlinkIfAllowed resolves a symlink to its target when\n// params.AllowSymlinkPath is true, or rejects it otherwise. When the input\n// is not a symlink, target is returned unchanged. A symlink that points to\n// another symlink is rejected so callers only deal with a single hop.\nfunc resolveSymlinkIfAllowed(target string, linfo fs.FileInfo, params AuditParams) (string, error) {\n\tif linfo.Mode()&os.ModeSymlink == 0 {\n\t\treturn target, nil\n\t}\n\tif !params.AllowSymlinkPath {\n\t\treturn \"\", fmt.Errorf(\"%s: path %q is a symlink (not allowed)\", params.Label, target)\n\t}\n\tresolved, err := vfs.EvalSymlinks(target)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%s: cannot resolve symlink %q: %w\", params.Label, target, err)","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/binding/audit.go#L70-L106","documentation":"lstatNonDir succeeded but the Lstat info shows the audited path is a directory, and the audit requires a regular file. The security audit deliberately refuses directories so bindings reference actual files.","triggerScenarios":"AssertSecurePath is called with a path that Lstat reports IsDir() — a directory was configured where a file is required (e.g. a directory named like the expected config file, or the user passed a folder path).","commonSituations":"Config value points at a directory (e.g. ~/.config/myapp/ instead of ~/.config/myapp/config.yaml); workspace layout change turned the expected file into a directory; accidental mkdir with the file's name.","solutions":["Point the binding at the concrete file inside the directory (append the filename).","Verify with 'ls -la' what exists at the path and correct the configured path.","If a directory is legitimately expected, this is the wrong audit/API — use a directory-aware path instead of AssertSecurePath for files.","Recreate the layout so the path is a regular file (e.g. remove the mistakenly created directory and restore the file)."],"exampleFix":"// before\npath := \"/home/u/.config/myapp\"      // directory\n// after\npath := \"/home/u/.config/myapp/config.yaml\"","handlingStrategy":"validation","validationCode":"// preflight: reject directories before the audit\nif info, err := os.Lstat(p); err == nil && info.IsDir() {\n    return fmt.Errorf(\"%q is a directory; point the binding at a concrete file inside it\", p)\n}","typeGuard":"func isDirErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"is a directory, not a file\")\n}","tryCatchPattern":"if err := binding.AssertSecurePath(p, params); err != nil {\n    if isDirErr(err) {\n        return fmt.Errorf(\"append the filename to the configured path: %w\", err)\n    }\n    return err\n}","preventionTips":["Configure full file paths, never directory paths, for file bindings.","Verify layout after workspace/version changes that may have turned files into directories.","Add config validation that lstats and rejects directories at load time.","Use ls -la to confirm the path is a regular file before deployment."],"tags":["filesystem","path-validation","security","go"],"backgroundTag":"path-is-a-directory","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"}