{"record":{"id":"2bae996c58e3c825","repo":"vxcontrol/pentagi","slug":"failed-to-bulk-update-assistants-provider-name-w","errorCode":null,"errorMessage":"failed to bulk-update assistants provider name: %w","messagePattern":"failed to bulk-update assistants provider name: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/flows.go","lineNumber":502,"sourceCode":"\tflows, flowsErr := fc.db.UpdateFlowsProviderNameByOldName(ctx, database.UpdateFlowsProviderNameByOldNameParams{\n\t\tNewName: newName.String(),\n\t\tUserID:  userID,\n\t\tOldName: oldName.String(),\n\t})\n\tif flowsErr != nil {\n\t\tlogger.WithError(flowsErr).Error(\"failed to bulk-update flows provider name\")\n\t\tflowsErr = fmt.Errorf(\"failed to bulk-update flows provider name: %w\", flowsErr)\n\t}\n\n\tassistants, asstErr := fc.db.UpdateAssistantsProviderNameByOldName(\n\t\tctx, database.UpdateAssistantsProviderNameByOldNameParams{\n\t\t\tNewName: newName.String(),\n\t\t\tUserID:  userID,\n\t\t\tOldName: oldName.String(),\n\t\t})\n\tif asstErr != nil {\n\t\tlogger.WithError(asstErr).Error(\"failed to bulk-update assistants provider name\")\n\t\tasstErr = fmt.Errorf(\"failed to bulk-update assistants provider name: %w\", asstErr)\n\t}\n\n\t// Publishing happens only after both writes are done. A subscriber that is\n\t// not draining its channel makes each publish cost up to the subscription\n\t// send timeout, so doing it in between would let a wedged websocket client\n\t// eat the deadline and starve the second UPDATE.\n\tfor _, flow := range flows {\n\t\t// Skipped rather than published with no containers: FlowUpdated carries\n\t\t// the full terminal list and the client replaces its cached value with\n\t\t// whatever arrives, so an empty list would wipe the flow's terminals in\n\t\t// the UI. Same handling as flowWorker.switchProvider.\n\t\tcontainers, err := fc.db.GetFlowContainers(ctx, flow.ID)\n\t\tif err != nil {\n\t\t\tlogger.WithError(err).Warnf(\"failed to get containers for flow %d, skipping its update event\", flow.ID)\n\t\t\tcontinue\n\t\t}\n\t\tfc.subs.NewFlowPublisher(userID, flow.ID).FlowUpdated(ctx, flow, containers)\n\t}","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/flows.go#L484-L520","documentation":"Raised inside reassignFlowsProvider when the bulk UPDATE of the assistants table — UpdateAssistantsProviderNameByOldName — fails while repointing a user's assistants from oldName to newName after a provider rename/deletion. Like the flows update, the SQL error is logged and wrapped as \"failed to bulk-update assistants provider name: %w\". Publishing of the provider-change event happens only after both writes complete so a wedged websocket subscriber cannot starve the second UPDATE.","triggerScenarios":"Calling RenameFlowsProvider or ResetFlowsProviderToDefault when UPDATE assistants SET ... WHERE user_id=? AND provider_name=oldName fails: DB unreachable, lock contention, or the detached reassignProviderTimeout context expiring (possibly after a slow publish on the flows path).","commonSituations":"Provider rename in the settings UI hitting a temporarily unhealthy DB; row-level locks held by concurrent assistant writes; DB failover between the two UPDATE statements; timeout too small for a loaded database.","solutions":["Check server logs for the logged underlying SQL error (asstErr is logged before wrapping).","Retry the rename — both sweeps match only rows still bearing oldName, so the operation is idempotent.","Investigate locks on the assistants table and concurrent transactions.","Verify DB connectivity/pool health and consider increasing reassignProviderTimeout."],"exampleFix":"null","handlingStrategy":"retry","validationCode":"// verify the old provider name really no longer resolves to skip a pointless cascade\nif _, err := provs.GetProvider(ctx, oldName, userID); err == nil {\n    return nil // old name still resolves; nothing to reassign\n}","typeGuard":"null","tryCatchPattern":"if err := flows.ResetFlowsProviderToDefault(ctx, userID, old, ptype); err != nil {\n    if strings.Contains(err.Error(), \"failed to bulk-update assistants provider name\") {\n        time.Sleep(backoff)\n        err = flows.ResetFlowsProviderToDefault(ctx, userID, old, ptype)\n    }\n    return err\n}","preventionTips":["Retry freely — the sweep only rewrites rows still bearing oldName (idempotent).","Check for long-running transactions holding locks on the assistants table.","Verify DB pool health; this fires on connectivity/timeout, not logic errors.","Remember errors from both tables are joined — always check for the flows-table error too."],"tags":["database","go","provider","bulk-update"],"backgroundTag":"bulk-update-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}