{"record":{"id":"823b5096d337be28","repo":"OpenNHP/opennhp","slug":"fail-to-check-resource-w","errorCode":null,"errorMessage":"fail to check resource: %w","messagePattern":"fail to check resource: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/kbs/resource/resource.go","lineNumber":180,"sourceCode":"\t}\n\n\tfullPath := filepath.Join(absBaseDir, resourceID)\n\n\tabsFullPath, err := filepath.Abs(fullPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fail to get resource absolute path: %w\", err)\n\t}\n\n\t// Check if the path is within the base directory to avoid path traversal attack.\n\tif !strings.HasPrefix(absFullPath, absBaseDir) {\n\t\treturn nil, errors.New(\"invalid resource ID: potential path traversal attack\")\n\t}\n\n\tif _, statErr := os.Stat(absFullPath); statErr != nil {\n\t\tif os.IsNotExist(statErr) {\n\t\t\treturn nil, errors.New(\"resource not found\")\n\t\t}\n\t\treturn nil, fmt.Errorf(\"fail to check resource: %w\", statErr)\n\t}\n\n\tdata, err := os.ReadFile(absFullPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fail to read resource: %w\", err)\n\t}\n\treturn data, nil\n}\n\nfunc encryptWithA256GCM(key, plaintext []byte) (ciphertext, iv, tag []byte, err error) {\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, nil, nil, err","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/kbs/resource/resource.go#L162-L198","documentation":"loadResource wraps any os.Stat error on the resolved resource path that is NOT a 'does not exist' error with 'fail to check resource: %w'. It exists so that genuinely missing files return the distinct 'resource not found' error while permission, I/O, or path-form problems surface with their underlying cause preserved via %w. Callers (GetResource) use this to map failures onto HTTP responses.","triggerScenarios":"os.Stat(absFullPath) fails with an error other than ENOENT — e.g. a permission error (EACCES) on the resource directory, the path is not reachable, an I/O error occurs, or a component of the path is not a directory (ENOTDIR). Called via GetResource when serving KBS resource requests.","commonSituations":"Resource files deployed with wrong ownership/permissions so the nhp-server process cannot stat them; the resource path contains a regular file used as a directory component; NFS/network mounts flaking; baseDir misconfigured to point into a restricted directory.","solutions":["Check permissions on the resource directory tree with `ls -la` and `sudo -u <serveruser> stat <path>`; fix with chmod/chown so the server user can traverse it.","Inspect the wrapped cause (%w) in the log to see the exact errno and address it (ENOTDIR, EACCES, EIO, etc.).","Verify baseDir configuration points at a directory that exists and is readable by the daemon.","If on a network mount, check mount health and remount; retry the request.","If the resource is genuinely gone, redeploy the resource file under baseDir."],"exampleFix":"// before: any stat failure surfaces as opaque 500\nif _, statErr := os.Stat(absFullPath); statErr != nil {\n\treturn nil, fmt.Errorf(\"fail to check resource: %w\", statErr)\n}\n// after: handle not-a-directory like not-found for clearer client errors\nif _, statErr := os.Stat(absFullPath); statErr != nil {\n\tif os.IsNotExist(statErr) || errors.Is(statErr, syscall.ENOTDIR) {\n\t\treturn nil, errors.New(\"resource not found\")\n\t}\n\treturn nil, fmt.Errorf(\"fail to check resource: %w\", statErr)\n}","handlingStrategy":"try-catch","validationCode":"path := filepath.Join(baseDir, resourceID)\nabs, _ := filepath.Abs(path)\nif _, err := os.Stat(abs); err != nil {\n\tif !os.IsNotExist(err) {\n\t\t// pre-check: surface stat problem early\n\t\tlog.Printf(\"resource path unusable: %v\", err)\n\t}\n}","typeGuard":"func isStatPermissionErr(err error) bool {\n\treturn errors.Is(err, os.ErrPermission)\n}","tryCatchPattern":"data, err := GetResource(id, token)\nif err != nil {\n\tvar statErr *os.PathError\n\tif errors.As(err, &statErr) && !os.IsNotExist(statErr) {\n\t\t// permissions/I-O issue: alert operator, return 500\n\t}\n}","preventionTips":["Run the daemon as a user with read access to baseDir.","Provision resource files with mode 0644 and directories 0755.","Avoid using files as directory components in resource IDs.","Health-check resource availability at startup, not per request.","Keep resources on local, reliably mounted storage."],"tags":["filesystem","stat","kbs","server"],"backgroundTag":"file-open-failed","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}