{"record":{"id":"7c2835a768338e2d","repo":"gastownhall/beads","slug":"db-labelsqlrepository-deleteallforids-from-s-w","errorCode":null,"errorMessage":"db: LabelSQLRepository.DeleteAllForIDs from %s: %w","messagePattern":"db: LabelSQLRepository\\.DeleteAllForIDs from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/label.go","lineNumber":218,"sourceCode":"\t\tif end > len(ids) {\n\t\t\tend = len(ids)\n\t\t}\n\t\tbatch := ids[start:end]\n\t\tplaceholders := make([]string, len(batch))\n\t\targs := make([]any, len(batch))\n\t\tfor i, id := range batch {\n\t\t\tplaceholders[i] = \"?\"\n\t\t\targs[i] = id\n\t\t}\n\t\t//nolint:gosec // G201: table is one of two hardcoded constants; ? placeholders only.\n\t\tres, err := r.runner.ExecContext(ctx,\n\t\t\tfmt.Sprintf(\"DELETE FROM %s WHERE issue_id IN (%s)\", table, strings.Join(placeholders, \",\")),\n\t\t\targs...)\n\t\tif err != nil {\n\t\t\tif opts.UseWispsTable && dberrors.IsTableNotExist(err) {\n\t\t\t\treturn total, nil\n\t\t\t}\n\t\t\treturn total, fmt.Errorf(\"db: LabelSQLRepository.DeleteAllForIDs from %s: %w\", table, err)\n\t\t}\n\t\tn, err := res.RowsAffected()\n\t\tif err != nil {\n\t\t\treturn total, fmt.Errorf(\"db: LabelSQLRepository.DeleteAllForIDs rows affected: %w\", err)\n\t\t}\n\t\ttotal += int(n)\n\t}\n\treturn total, nil\n}\n\nfunc (r *labelSQLRepositoryImpl) CountAllForIDs(ctx context.Context, ids []string, opts domain.LabelOpts) (int, error) {\n\tif len(ids) == 0 {\n\t\treturn 0, nil\n\t}\n\ttable := \"labels\"\n\tif opts.UseWispsTable {\n\t\ttable = \"wisp_labels\"\n\t}","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/label.go#L200-L236","documentation":"LabelSQLRepository.DeleteAllForIDs failed to execute its DELETE for one of the target label tables. The repository returns the running total alongside the wrapped driver error. Note the wisps-table 'table not exist' case is treated as success, so this error is a real DELETE failure for a table that does exist (or a non-wisps table).","triggerScenarios":"Calling DeleteAllForIDs when the DELETE statement fails: table missing in non-wisps mode, SQL error, connection failure, or lock conflict.","commonSituations":"Schema migrations not run so the labels table is absent; database locked by another writer; too many IDs generating too many placeholders; storage file corruption in embedded mode.","solutions":["Unwrap the error to see the driver cause (no such table, locked, too many params)","Run schema migrations so the labels table exists before deleting","Batch the ids slice if the IN-clause exceeds driver parameter limits","Check for concurrent writers/locks on the database and retry when idle"],"exampleFix":"// before\nn, err := repo.DeleteAllForIDs(ctx, allIDs, opts)\n// after\nfor i := 0; i < len(allIDs); i += 500 {\n    if _, err := repo.DeleteAllForIDs(ctx, allIDs[i:min(i+500, len(allIDs))], opts); err != nil { ... }\n}","handlingStrategy":"validation","validationCode":"// ensure table exists and batch ids\nif len(ids) == 0 { return 0, nil }\nfor start := 0; start < len(ids); start += 500 { _ = ids[start:min(start+500, len(ids))] }","typeGuard":null,"tryCatchPattern":"n, err := repo.DeleteAllForIDs(ctx, ids, opts)\nif err != nil {\n    if dberrors.IsTableNotExist(errors.Unwrap(err)) && opts.UseWispsTable {\n        return 0, nil // treat as no-op\n    }\n    return err\n}","preventionTips":["Run migrations so labels tables exist before deletes","Batch ID slices under parameter limits","Avoid concurrent writers contending for locks","Unwrap and log the driver cause for diagnostics"],"tags":["go","database","sql","delete"],"backgroundTag":"sql-delete-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}