{"record":{"id":"3c3e3db2b403ec8a","repo":"vxcontrol/pentagi","slug":"failed-to-create-resources-directory-w","errorCode":null,"errorMessage":"failed to create resources directory: %w","messagePattern":"failed to create resources directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"backend/pkg/resources/resources.go","lineNumber":66,"sourceCode":"\n// ResourcesDir returns the absolute path to the flat blob storage directory.\nfunc ResourcesDir(dataDir string) string {\n\treturn filepath.Join(dataDir, ResourcesDirName)\n}\n\n// BlobPath returns the absolute path to the .blob file for a given MD5 hash.\nfunc BlobPath(dataDir, hash string) string {\n\tcleanHash := strings.ToLower(strings.TrimSpace(hash))\n\tif !IsValidBlobHash(cleanHash) {\n\t\treturn filepath.Join(ResourcesDir(dataDir), invalidBlobHashFileName)\n\t}\n\treturn filepath.Join(ResourcesDir(dataDir), cleanHash+\".blob\")\n}\n\n// EnsureResourcesDir creates the resources storage directory if it does not exist.\nfunc EnsureResourcesDir(dataDir string) error {\n\tif err := os.MkdirAll(ResourcesDir(dataDir), 0755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create resources directory: %w\", err)\n\t}\n\treturn nil\n}\n\n// ComputeFileMD5 reads r to EOF and returns the lowercase hex MD5 digest.\nfunc ComputeFileMD5(r io.Reader) (string, error) {\n\th := md5.New()\n\tif _, err := io.Copy(h, r); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to compute MD5: %w\", err)\n\t}\n\treturn hex.EncodeToString(h.Sum(nil)), nil\n}\n\n// IsValidBlobHash reports whether hash is a hex-encoded MD5 digest.\nfunc IsValidBlobHash(hash string) bool {\n\tif len(hash) != md5.Size*2 {\n\t\treturn false\n\t}","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/resources/resources.go#L48-L84","documentation":"EnsureResourcesDir creates the resources blob-storage directory (<dataDir>/resources, mode 0755) via os.MkdirAll and wraps any OS error with this message. Callers (CommitBlob, promoteToResources, UploadResources) invoke it before writing blob files, so failure here means the storage root cannot be prepared and the resource write must abort.","triggerScenarios":"os.MkdirAll(ResourcesDir(dataDir), 0755) returns a non-nil error at resources.go:65 — the path's parent exists but is a file (ENOTDIR), a permissions problem (EACCES/EACCES on mkdir), a read-only filesystem (EROFS), or disk/device errors. dataDir comes from configuration.","commonSituations":"DATA_DIR env var misconfigured to a path that is a regular file or does not exist with unwritable parents; running the container/binary as a non-root user against a root-owned data directory; read-only container volume mount on the data dir; disk full or I/O errors on the host.","solutions":["Check the wrapped %w cause and errno to identify the OS-level reason.","Verify the configured data dir (DATA_DIR / config value) points to a writable directory, not a file: ls -ld <dataDir> and check it is a directory.","Fix permissions/ownership: chown or chmod the data directory (or its parent) for the user running the process.","If running in Docker, ensure the data volume is not mounted read-only (remove :ro from docker-compose).","Free disk space / check mount health (df -h, dmesg) if the errno is ENOSPC or EIO."],"exampleFix":"// before: data dir is a file, so MkdirAll fails with ENOTDIR\n$ touch /var/lib/pentagi/data\nDATA_DIR=/var/lib/pentagi/data\n// after: data dir is a real, writable directory\n$ rm /var/lib/pentagi/data && mkdir -p /var/lib/pentagi/data && chown pentagi:pentagi /var/lib/pentagi/data\nDATA_DIR=/var/lib/pentagi/data","handlingStrategy":"try-catch","validationCode":"// check writability before attempting blob writes\ndir := resources.ResourcesDir(dataDir)\nif fi, err := os.Stat(dir); err == nil && !fi.IsDir() {\n    return fmt.Errorf(\"data dir path %s exists but is not a directory\", dir)\n}\nif err := resources.EnsureResourcesDir(dataDir); err != nil {\n    return fmt.Errorf(\"storage not writable: %w\", err)\n}","typeGuard":"func resourcesDirReady(dataDir string) bool {\n    fi, err := os.Stat(resources.ResourcesDir(dataDir))\n    return err == nil && fi.IsDir()\n}","tryCatchPattern":"if err := resources.EnsureResourcesDir(cfg.DataDir); err != nil {\n    var pe *fs.PathError\n    if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrPermission) {\n        log.Fatalf(\"cannot create resources dir %s: permission denied — fix DATA_DIR ownership/mount\", cfg.DataDir)\n    }\n    return fmt.Errorf(\"failed to create resources directory: %w\", err)\n}","preventionTips":["Validate DATA_DIR at startup (exists, is a directory, writable via a probe file) before serving traffic","Run the process with a user that owns or has write access to the data directory","In Docker, mount the data volume without :ro and set correct ownership on the host path","Check disk space monitoring/alerting on the data volume","Wrap and log errors.Unwrap to surface the fs.PathError errno in operations runbooks"],"tags":["go","filesystem","configuration","docker"],"backgroundTag":"mkdir-permission-denied","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}