Billionmail/BillionMail · error

error removing knowledge base file: %v

Error message

error removing knowledge base file: %v

What it means

This error wraps os.Remove failure when deleting the knowledge base JSON file. The FileExists check passed, so the file existed at check time but the removal still failed — typically a permission problem, the path being a directory, or the file being locked/mounted. There is also an inherent TOCTOU race: the file can vanish between FileExists and Remove.

Source

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

	knowledge.UpdateTime = public.GetNowTime()
	err = SaveKnowledgeBase(Domain, knowledge)
	if err != nil {
		return fmt.Errorf("error saving knowledge base: %v", err)
	}
	return nil
}

// RemoveKnowledgeBaseFile removes a knowledge base file based on the provided domain and knowledge ID.
// It constructs the file path, checks if the file exists, and removes it.
func RemoveKnowledgeBaseFile(Domain string, Kid string) error {
	filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/knowledge/%s.json", Domain, Kid)
	if !public.FileExists(filename) {
		return fmt.Errorf("knowledge base file does not exist: %s", filename)
	}
	err := os.Remove(filename)
	if err != nil {
		return fmt.Errorf("error removing knowledge base file: %v", err)
	}
	return nil
}

// ReadCompanyProfile reads the company profile from a JSON file based on the provided domain.
// It returns a CompanyProfile struct or an error if the file does not exist or cannot be read.
func ReadCompanyProfile(Domain string) (CompanyProfile, error) {
	filename := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/company_profile.json", Domain)
	if !public.FileExists(filename) {
		companyProfileDefault := CompanyProfile{
			LegalCompanyName: "",
			WebSite:          "",
			CompanyProfile:   "",
			Email:            "",
			Phone:            "",
			SupportUrl:       "",
		}
		// If the company profile file does not exist, return a default profile

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Read the wrapped %v error for the OS cause (EACCES, EISDIR, ENOENT)
  2. Fix permissions/ownership on the file or its parent directory
  3. If a directory occupies the name, remove it manually or use os.RemoveAll appropriately
  4. Make removal idempotent: use os.Remove directly and treat ENOENT as success, eliminating the FileExists race

Example fix

// before
if !public.FileExists(filename) {
    return fmt.Errorf("knowledge base file does not exist: %s", filename)
}
err := os.Remove(filename)
// after
if err := os.Remove(filename); err != nil && !os.IsNotExist(err) {
    return fmt.Errorf("error removing knowledge base file: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

p := fmt.Sprintf(PRODUCT_CONFIG_PATH+"/%s/knowledge/%s.json", domain, kid)
if fi, err := os.Stat(p); err == nil && fi.IsDir() {
    return fmt.Errorf("%s is a directory, not a file", p)
}
if err := syscall.Access(filepath.Dir(p), unix.W_OK); err != nil {
    return fmt.Errorf("no write permission on %s", filepath.Dir(p))
}

Try / catch

if err := RemoveKnowledgeBaseFile(domain, kid); err != nil {
    if strings.Contains(err.Error(), "removing knowledge base file") {
        log.Errorf("Delete failed for %s/%s: %v — check perms/locks", domain, kid, err)
        // retry with backoff or alert ops
    }
}

Prevention

When it happens

Trigger: os.Remove(filename) fails despite FileExists returning true: permission denied on the file or its parent directory; <kid>.json is actually a directory; the file was deleted between the check and the remove (race); filesystem is read-only after the check.

Common situations: File owned by another user (root-created, service running unprivileged); a directory accidentally created with the .json name; concurrent delete requests racing; immutable files or read-only remounts.

Related errors


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