Billionmail/BillionMail · error

Cannot delete the system's built-in model supplier.

Error message

Cannot delete the system's built-in model supplier.

What it means

RemoveSupplier refuses to delete a supplier whose name also exists under SUPPLIER_TEMPLATE_PATH, because such suppliers are system built-ins shipped with the application. Deleting them would break default functionality and they would be restored on upgrade anyway.

Source

Thrown at core/internal/service/askai/supplier.go:630

		Sort:            100, // Default sort order, can be changed later
		Icon:            "",  // Default icon, can be set later
	}

	return SaveSupplierConfig(supplierName, supplier)
}

// RemoveSupplier deletes a supplier's configuration directory and all its contents.
// It checks if the supplier exists, and if so, it removes the directory and its files
// If the supplier does not exist, it returns an error indicating that the supplier cannot be found.
// This function is useful for cleaning up unused suppliers from the configuration.
func RemoveSupplier(supplierName string) error {
	supplierPath := SUPPLIER_CONFIG_PATH + "/" + supplierName
	if !public.FileExists(supplierPath) {
		return errors.New("supplier does not exist")
	}
	templatePath := SUPPLIER_TEMPLATE_PATH + "/" + supplierName
	if public.FileExists(templatePath) {
		return errors.New("Cannot delete the system's built-in model supplier.")
	}

	err := os.RemoveAll(supplierPath)
	if err != nil {
		return err
	}

	return nil
}

// SetModelTitle updates the title of a model in the supplier's models.json file.
// It reads the existing models, modifies the title of the specified model, and saves the updated list back to the file.
// If the model is not found, it returns an error indicating that the model does not exist.
func SetModelTitle(supplierName string, modelId string, title string) error {
	modelsFile := SUPPLIER_CONFIG_PATH + "/" + supplierName + "/models.json"
	if !public.FileExists(modelsFile) {
		return errors.New("models file does not exist for supplier: " + supplierName)
	}

View on GitHub (pinned to fc36c76c05)

Solutions

  1. Only remove custom suppliers — list SUPPLIER_CONFIG_PATH entries that have no counterpart in SUPPLIER_TEMPLATE_PATH and skip built-ins
  2. If the goal is to disable a built-in supplier, call SetSupplierStatus(name, false) instead of deleting it
  3. If credentials must change, overwrite the built-in's config (SaveSupplierConfig) rather than removing it

Example fix

// before
for _, s := range allSuppliers {
    RemoveSupplier(s) // fails on built-ins
}

// after
for _, s := range allSuppliers {
    if !public.FileExists(SUPPLIER_TEMPLATE_PATH+"/"+s) {
        RemoveSupplier(s)
    } else {
        SetSupplierStatus(s, false) // disable built-ins instead
    }
}
Defensive patterns

Strategy: validation

Validate before calling

func isBuiltinSupplier(name string) bool {
    return public.FileExists(SUPPLIER_TEMPLATE_PATH + "/" + name)
}
// only delete customs:
if !isBuiltinSupplier(name) { RemoveSupplier(name) }

Try / catch

if err := RemoveSupplier(name); err != nil {
    if strings.Contains(err.Error(), "built-in") {
        // disable instead: SetSupplierStatus(name, false)
    } else { return err }
}

Prevention

When it happens

Trigger: Calling RemoveSupplier on any built-in supplier (one that has a directory under SUPPLIER_TEMPLATE_PATH), even though its config directory exists.

Common situations: Trying to clean up 'unused' suppliers that are actually default built-ins; scripting bulk deletion of all suppliers; users wanting to reset a built-in supplier's credentials.

Related errors


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