{"record":{"id":"6fa888e61912ecae","repo":"Billionmail/BillionMail","slug":"error-reading-knowledge-base-file-v","errorCode":null,"errorMessage":"error reading knowledge base file: %v","messagePattern":"error reading knowledge base file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/askai/project.go","lineNumber":296,"sourceCode":"\t}\n\n\terr = SaveProjectConfig(Domain, config)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error saving project configuration: %v\", err)\n\t}\n\treturn nil\n}\n\n// ReadKnowledgeBase reads a specific knowledge base from a JSON file based on the provided domain and knowledge ID.\n// It returns a KnowledgeInfo struct or an error if the file does not exist or cannot be\nfunc ReadKnowledgeBase(Domain string, Kid string) (KnowledgeInfo, error) {\n\tfilename := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/knowledge/%s.json\", Domain, Kid)\n\tif !public.FileExists(filename) {\n\t\treturn KnowledgeInfo{}, fmt.Errorf(\"knowledge base file does not exist: %s\", filename)\n\t}\n\tdata, err := os.ReadFile(filename)\n\tif err != nil {\n\t\treturn KnowledgeInfo{}, fmt.Errorf(\"error reading knowledge base file: %v\", err)\n\t}\n\n\tvar knowledge KnowledgeInfo\n\terr = json.Unmarshal(data, &knowledge)\n\tif err != nil {\n\t\treturn KnowledgeInfo{}, fmt.Errorf(\"error unmarshalling knowledge base: %v\", err)\n\t}\n\treturn knowledge, nil\n}\n\n// SaveKnowledgeBase saves the provided KnowledgeInfo to a JSON file based on the domain and knowledge ID.\n// It creates the directory if it does not exist and writes the knowledge information to a JSON file\nfunc SaveKnowledgeBase(Domain string, knowledge KnowledgeInfo) error {\n\tknowledgePath := fmt.Sprintf(\"%s/%s/knowledge\", PRODUCT_CONFIG_PATH, Domain)\n\tif !public.FileExists(knowledgePath) {\n\t\tos.MkdirAll(knowledgePath, os.ModePerm)\n\t}\n\tfilename := fmt.Sprintf(\"%s/%s.json\", knowledgePath, knowledge.Kid)","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/askai/project.go#L278-L314","documentation":"After confirming the knowledge file exists, ReadKnowledgeBase reads it with os.ReadFile. If the OS read fails (permission denied, I/O error, file removed between the existence check and read), the error is wrapped as 'error reading knowledge base file: %v'. This is distinct from the not-exist error and signals an actual read operation failure.","triggerScenarios":"ReadKnowledgeBase(Domain, Kid) where FileExists passed but os.ReadFile fails: permission change, file deleted in TOCTOU window, I/O error, or path is a directory not a file.","commonSituations":"Race with a concurrent delete/save; root-owned file read by an unprivileged process; Kid resolving to a directory named '<kid>.json'; NFS/storage transient failures.","solutions":["Check permissions on the knowledge JSON file for the process user","Re-run the request; a TOCTOU race (file deleted between check and read) is transient","Verify the path is a regular file, not a directory (os.Stat / mode check)","Inspect the wrapped inner error (syscall text) for the exact cause","Restore or re-save the knowledge file if it was removed"],"exampleFix":"// before\nkb, err := askai.ReadKnowledgeBase(domain, kid)\n// after\nkb, err := askai.ReadKnowledgeBase(domain, kid)\nif err != nil && strings.Contains(err.Error(), \"reading knowledge base file\") {\n    time.Sleep(100 * time.Millisecond) // transient race?\n    kb, err = askai.ReadKnowledgeBase(domain, kid)\n}","handlingStrategy":"retry","validationCode":"kbPath := fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/knowledge/%s.json\", domain, kid)\nfi, err := os.Stat(kbPath)\nif err != nil || fi.IsDir() {\n    return fmt.Errorf(\"knowledge %s not a readable file\", kid)\n}\nif f, err := os.Open(kbPath); err != nil {\n    return err\n} else {\n    f.Close()\n}","typeGuard":"func knowledgeReadable(domain, kid string) bool {\n    f, err := os.Open(fmt.Sprintf(PRODUCT_CONFIG_PATH+\"/%s/knowledge/%s.json\", domain, kid))\n    if err != nil { return false }\n    f.Close()\n    return true\n}","tryCatchPattern":"var kb askai.KnowledgeInfo\nvar err error\nfor i := 0; i < 3; i++ {\n    kb, err = askai.ReadKnowledgeBase(domain, kid)\n    if err == nil || !strings.Contains(err.Error(), \"reading knowledge base file\") {\n        break\n    }\n    time.Sleep(100 * time.Millisecond << i)\n}","preventionTips":["Use atomic writes (temp + rename) so readers never see partial files","Keep knowledge files owned/readable by the service user","Add small retry with backoff for transient I/O errors","Verify paths resolve to regular files, not directories","Log syscall-level inner errors for storage diagnostics"],"tags":["go","file-io","knowledge-base"],"backgroundTag":"file-read-failed","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}