{"record":{"id":"9192def8e3ef44c2","repo":"plandex-ai/plandex","slug":"error-deleting-custom-providers-v","errorCode":null,"errorMessage":"error deleting custom providers: %v","messagePattern":"error deleting custom providers: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/models.go","lineNumber":189,"sourceCode":"\tvar providers []*CustomProvider\n\terr := Conn.Select(&providers, `SELECT * FROM custom_providers WHERE org_id = $1 ORDER BY name`, orgId)\n\treturn providers, err\n}\n\nfunc ListCustomProvidersForNames(orgId string, names []string) ([]*CustomProvider, error) {\n\tvar providers []*CustomProvider\n\tquery := `SELECT * FROM custom_providers WHERE org_id = $1 AND name = ANY($2) ORDER BY name`\n\terr := Conn.Select(&providers, query, orgId, pq.Array(names))\n\treturn providers, err\n}\n\nfunc DeleteCustomProviders(tx *sqlx.Tx, orgId string, ids []string) error {\n\tif tx == nil {\n\t\treturn fmt.Errorf(\"tx is nil\")\n\t}\n\t_, err := tx.Exec(`DELETE FROM custom_providers WHERE org_id = $1 AND id = ANY($2)`, orgId, pq.Array(ids))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error deleting custom providers: %v\", err)\n\t}\n\treturn nil\n}\n\nfunc UpsertModelPack(tx *sqlx.Tx, mp *ModelPack) error {\n\tif tx == nil {\n\t\treturn fmt.Errorf(\"tx is nil\")\n\t}\n\tconst q = `\nINSERT INTO model_sets (\n\t  org_id, name, description,\n\t  planner, coder, plan_summary,\n\t  builder, whole_file_builder, namer,\n\t  commit_msg, exec_status, context_loader\n)\nVALUES (\n\t  $1,$2,$3,\n\t  $4,$5,$6,","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/models.go#L171-L207","documentation":"DeleteCustomProviders wraps any failure of the batched DELETE on custom_providers (scoped by org_id and id ANY($2)) with this message. Zero rows deleted is not an error; this only fires when the SQL execution itself fails. The wrapped error is the lib/pq driver or Postgres error.","triggerScenarios":"Dead/aborted transaction passed to DeleteCustomProviders; FK constraint referencing custom_providers rows; missing DELETE privilege; custom_providers table absent due to skipped migrations; malformed ids for pq.Array.","commonSituations":"Staging DB without latest migrations; production role without DELETE grants; a long-running tx hit a deadlock or statement_timeout; custom providers still referenced by models.","solutions":["Read the wrapped error code (pq/pgerror) and verify the table and constraints (\\d custom_providers)","If the transaction is aborted from a prior failure, roll back and start a new tx","Grant DELETE on custom_providers to the application role","Remove/cascade dependent references before deleting providers","Check logs for deadlock/timeout and retry the whole transaction"],"exampleFix":"// before\n_, err := tx.Exec(`DELETE FROM custom_providers WHERE org_id = $1 AND id = ANY($2)`, orgId, pq.Array(ids))\nif err != nil { return fmt.Errorf(\"error deleting custom providers: %v\", err) }\n// after\n_, err := tx.Exec(`DELETE FROM custom_providers cp USING models m WHERE cp.id = m.provider_id AND ...`) // delete dependents first\n_, err = tx.Exec(`DELETE FROM custom_providers WHERE org_id = $1 AND id = ANY($2)`, orgId, pq.Array(ids))","handlingStrategy":"try-catch","validationCode":"func validateDeleteCustomProviders(tx *sqlx.Tx, orgId string, ids []string) error {\n    if tx == nil { return errors.New(\"tx is nil\") }\n    if len(ids) == 0 { return errors.New(\"no provider ids given\") }\n    return nil\n}","typeGuard":"func isForeignKeyViolation(err error) bool {\n    var pgErr *pgconn.PgError\n    return errors.As(err, &pgErr) && pgErr.Code == \"23503\"\n}","tryCatchPattern":"if err := db.DeleteCustomProviders(tx, orgId, ids); err != nil {\n    if isForeignKeyViolation(err) { return fmt.Errorf(\"providers still referenced: %w\", err) }\n    return err\n}","preventionTips":["Check for dependent models before deleting providers","Roll back and retry in a fresh tx after any prior statement failure","Verify custom_providers exists via migrations in every environment","Ensure the DB role has DELETE privileges","Use pq.Array consistently for id arrays"],"tags":["database","postgres","sqlx","transaction"],"backgroundTag":"sql-delete-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}