Billionmail/BillionMail · error
%s
Error message
%s
What it means
When any per-file rollback error was collected, rollback returns a single error whose message is the semicolon-joined list of all failure messages. The '%s' format is just an aggregator — the real causes are the embedded 'failed to read backup file ...' / 'failed to rollback file ...' strings inside it.
Source
Thrown at core/internal/service/multi_ip_domain/config_manager.go:124
var allErrors []string
for file, backup := range backups {
if gfile.Exists(backup) {
g.Log().Infof(ctx, "Rolling back %s from %s", file, backup)
content, err := ioutil.ReadFile(backup)
if err != nil {
err = fmt.Errorf("failed to read backup file %s: %v", backup, err)
allErrors = append(allErrors, err.Error())
continue
}
if err := ioutil.WriteFile(file, content, 0644); err != nil {
err = fmt.Errorf("failed to rollback file %s: %v", file, err)
allErrors = append(allErrors, err.Error())
}
}
}
if len(allErrors) > 0 {
return fmt.Errorf("%s", strings.Join(allErrors, "; "))
}
return nil
}
// cleanupBackups Clean up backup files
func (m *ConfigManager) cleanupBackups(ctx context.Context, backups map[string]string) {
for _, backup := range backups {
if gfile.Exists(backup) {
g.Log().Debugf(ctx, "Cleaning up backup file: %s", backup)
_ = os.Remove(backup)
}
}
}
// applyConfigs Apply configurations
func (m *ConfigManager) applyConfigs(ctx context.Context, configs []map[string]interface{}) error {
// 1. Update docker-compose.yml
if err := m.updateDockerCompose(ctx, configs); err != nil {View on GitHub (pinned to fc36c76c05)
Solutions
- Split the message on '; ' to enumerate the individual per-file failures and address each (permissions, disk, missing backup).
- Manually restore each affected config from its .backup.<nano> file.
- Fix the underlying filesystem condition, then re-run ApplyConfigsWithRollback to reach a clean state.
- Prefer restructuring to return errors.Join/[]error instead of a joined string for programmatic handling.
Example fix
// before
if len(allErrors) > 0 {
return fmt.Errorf("%s", strings.Join(allErrors, "; "))
}
// after
if len(allErrors) > 0 {
return gerror.New("rollback failed for one or more files: " + strings.Join(allErrors, "; "))
} Defensive patterns
Strategy: try-catch
Try / catch
if err := mgr.ApplyConfigsWithRollback(ctx, configs); err != nil {
if strings.Contains(err.Error(), "rollback also failed") {
for _, part := range strings.Split(err.Error(), "; ") {
log.Printf("rollback sub-failure: %s", part)
}
}
} Prevention
- Parse the '; '-joined message to enumerate individual failures.
- Treat this wrapper as a signal to manually reconcile configs from backups.
- Prefer upstream change returning errors.Join for structured handling.
- After recovery, re-run the apply once the filesystem is healthy.
When it happens
Trigger: At least one backup read or restore-write failed during rollback after a failed apply; the joined messages surface as 'failed to apply configurations: <applyErr>, rollback also failed: <joined>'.
Common situations: Mixed failures: one backup file unreadable and another config file unwritable; caller sees this wrapper and must parse the joined string to find the root causes.
Related errors
- failed to read backup file %s: %v
- failed to rollback file %s: %v
- DNS automated resolution failed: ClientID, ClientSecret or T
- DNS automated resolution failed: APIKey or APISecret is empt
- Failed to get configuration
AI-assisted analysis of Billionmail/BillionMail@fc36c76c05 (2026-09-05).
Data as JSON: /api/errors/44fa5f80f606cf4c.
Report an issue: GitHub.