{"record":{"id":"44fa5f80f606cf4c","repo":"Billionmail/BillionMail","slug":"s-44fa5f","errorCode":null,"errorMessage":"%s","messagePattern":"%s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/internal/service/multi_ip_domain/config_manager.go","lineNumber":124,"sourceCode":"\tvar allErrors []string\n\tfor file, backup := range backups {\n\t\tif gfile.Exists(backup) {\n\t\t\tg.Log().Infof(ctx, \"Rolling back %s from %s\", file, backup)\n\t\t\tcontent, err := ioutil.ReadFile(backup)\n\t\t\tif err != nil {\n\t\t\t\terr = fmt.Errorf(\"failed to read backup file %s: %v\", backup, err)\n\t\t\t\tallErrors = append(allErrors, err.Error())\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := ioutil.WriteFile(file, content, 0644); err != nil {\n\t\t\t\terr = fmt.Errorf(\"failed to rollback file %s: %v\", file, err)\n\t\t\t\tallErrors = append(allErrors, err.Error())\n\t\t\t}\n\t\t}\n\t}\n\tif len(allErrors) > 0 {\n\t\treturn fmt.Errorf(\"%s\", strings.Join(allErrors, \"; \"))\n\t}\n\treturn nil\n}\n\n// cleanupBackups Clean up backup files\nfunc (m *ConfigManager) cleanupBackups(ctx context.Context, backups map[string]string) {\n\tfor _, backup := range backups {\n\t\tif gfile.Exists(backup) {\n\t\t\tg.Log().Debugf(ctx, \"Cleaning up backup file: %s\", backup)\n\t\t\t_ = os.Remove(backup)\n\t\t}\n\t}\n}\n\n// applyConfigs Apply configurations\nfunc (m *ConfigManager) applyConfigs(ctx context.Context, configs []map[string]interface{}) error {\n\t// 1. Update docker-compose.yml\n\tif err := m.updateDockerCompose(ctx, configs); err != nil {","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/Billionmail/BillionMail/blob/fc36c76c050c3775c5e899faf7403cf0262d2744/core/internal/service/multi_ip_domain/config_manager.go#L106-L142","documentation":"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.","triggerScenarios":"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>'.","commonSituations":"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.","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."],"exampleFix":"// before\nif len(allErrors) > 0 {\n\treturn fmt.Errorf(\"%s\", strings.Join(allErrors, \"; \"))\n}\n// after\nif len(allErrors) > 0 {\n\treturn gerror.New(\"rollback failed for one or more files: \" + strings.Join(allErrors, \"; \"))\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := mgr.ApplyConfigsWithRollback(ctx, configs); err != nil {\n\tif strings.Contains(err.Error(), \"rollback also failed\") {\n\t\tfor _, part := range strings.Split(err.Error(), \"; \") {\n\t\t\tlog.Printf(\"rollback sub-failure: %s\", part)\n\t\t}\n\t}\n}","preventionTips":["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."],"tags":["error-handling","rollback","go"],"backgroundTag":"aggregated-rollback-errors","analyzedSha":"fc36c76c050c3775c5e899faf7403cf0262d2744","analyzedAt":"2026-09-05T21:28:54.019Z","contentChangedAt":"2026-09-05T21:28:54.019Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}