Billionmail/BillionMail · error

error reading knowledge base file: %v

Error message

error reading knowledge base file: %v

What it means

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.

Source

Thrown at core/internal/service/askai/project.go:296

	}

	err = SaveProjectConfig(Domain, config)
	if err != nil {
		return fmt.Errorf("error saving project configuration: %v", err)
	}
	return nil
}

// ReadKnowledgeBase reads a specific knowledge base from a JSON file based on the provided domain and knowledge ID.
// It returns a KnowledgeInfo struct or an error if the file does not exist or cannot be
func ReadKnowledgeBase(Domain string, Kid string) (KnowledgeInfo, error) {
	filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/knowledge/%s.json", Domain, Kid)
	if !public.FileExists(filename) {
		return KnowledgeInfo{}, fmt.Errorf("knowledge base file does not exist: %s", filename)
	}
	data, err := os.ReadFile(filename)
	if err != nil {
		return KnowledgeInfo{}, fmt.Errorf("error reading knowledge base file: %v", err)
	}

	var knowledge KnowledgeInfo
	err = json.Unmarshal(data, &knowledge)
	if err != nil {
		return KnowledgeInfo{}, fmt.Errorf("error unmarshalling knowledge base: %v", err)
	}
	return knowledge, nil
}

// SaveKnowledgeBase saves the provided KnowledgeInfo to a JSON file based on the domain and knowledge ID.
// It creates the directory if it does not exist and writes the knowledge information to a JSON file
func SaveKnowledgeBase(Domain string, knowledge KnowledgeInfo) error {
	knowledgePath := fmt.Sprintf("%s/%s/knowledge", PRODUCT_CONFIG_PATH, Domain)
	if !public.FileExists(knowledgePath) {
		os.MkdirAll(knowledgePath, os.ModePerm)
	}
	filename := fmt.Sprintf("%s/%s.json", knowledgePath, knowledge.Kid)

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Check permissions on the knowledge JSON file for the process user
  2. Re-run the request; a TOCTOU race (file deleted between check and read) is transient
  3. Verify the path is a regular file, not a directory (os.Stat / mode check)
  4. Inspect the wrapped inner error (syscall text) for the exact cause
  5. Restore or re-save the knowledge file if it was removed

Example fix

// before
kb, err := askai.ReadKnowledgeBase(domain, kid)
// after
kb, err := askai.ReadKnowledgeBase(domain, kid)
if err != nil && strings.Contains(err.Error(), "reading knowledge base file") {
    time.Sleep(100 * time.Millisecond) // transient race?
    kb, err = askai.ReadKnowledgeBase(domain, kid)
}
Defensive patterns

Strategy: retry

Validate before calling

kbPath := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/knowledge/%s.json", domain, kid)
fi, err := os.Stat(kbPath)
if err != nil || fi.IsDir() {
    return fmt.Errorf("knowledge %s not a readable file", kid)
}
if f, err := os.Open(kbPath); err != nil {
    return err
} else {
    f.Close()
}

Type guard

func knowledgeReadable(domain, kid string) bool {
    f, err := os.Open(fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/knowledge/%s.json", domain, kid))
    if err != nil { return false }
    f.Close()
    return true
}

Try / catch

var kb askai.KnowledgeInfo
var err error
for i := 0; i < 3; i++ {
    kb, err = askai.ReadKnowledgeBase(domain, kid)
    if err == nil || !strings.Contains(err.Error(), "reading knowledge base file") {
        break
    }
    time.Sleep(100 * time.Millisecond << i)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05). Data as JSON: /api/errors/6fa888e61912ecae. Report an issue: GitHub.