{"record":{"id":"185f23b8aa510124","repo":"gastownhall/beads","slug":"get-next-child-id-update-counter-w","errorCode":null,"errorMessage":"get next child ID: update counter: %w","messagePattern":"get next child ID: update counter: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/child_id.go","lineNumber":58,"sourceCode":"\t\t\treturn \"\", fmt.Errorf(\"get next child ID: scan child row: %w\", err)\n\t\t}\n\t\t_, childNum, ok := ParseHierarchicalID(id)\n\t\tif ok && childNum > lastChild {\n\t\t\tlastChild = childNum\n\t\t}\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"get next child ID: iterate children: %w\", err)\n\t}\n\n\tnextChild := lastChild + 1\n\n\t//nolint:gosec // G201: counterTable is one of two hardcoded constants.\n\tif _, err := tx.ExecContext(ctx, fmt.Sprintf(`\n\t\tINSERT INTO %s (parent_id, last_child) VALUES (?, ?)\n\t\tON DUPLICATE KEY UPDATE last_child = ?\n\t`, counterTable), parentID, nextChild, nextChild); err != nil {\n\t\treturn \"\", fmt.Errorf(\"get next child ID: update counter: %w\", err)\n\t}\n\n\treturn fmt.Sprintf(\"%s.%d\", parentID, nextChild), nil\n}\n","sourceCodeStart":40,"sourceCodeEnd":63,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/child_id.go#L40-L63","documentation":"GetNextChildIDTx persists the incremented counter via INSERT ... ON DUPLICATE KEY UPDATE on the counter table; any failure is wrapped as \"get next child ID: update counter: %w\". If this fails, the child ID was computed but not created and the transaction should roll back. Called from ExecuteCreate.","triggerScenarios":"Updating the counter when: the counter table is missing, a lock-wait/deadlock occurs with a concurrent creator of the same parent, the transaction is aborted, or the connection drops.","commonSituations":"Two agents concurrently creating sub-issues under the same parent (lock contention on the counter row); unmigrated schema; long transactions hitting innodb_lock_wait_timeout.","solutions":["Retry the create transaction; the ON DUPLICATE KEY UPDATE is idempotent so a retry recomputes the correct next child.","Check wrapped error for 'lock wait timeout' or 'deadlock' and serialize creation under the same parent.","Run schema migrations so the counter table exists.","Keep the parent-create transaction short to reduce counter-row contention."],"exampleFix":"// before\nid, err := issueops.GetNextChildIDTx(ctx, tx, parentID)\nif err != nil { return err } // dead transaction reused later\n// after\nid, err := issueops.GetNextChildIDTx(ctx, tx, parentID)\nif err != nil {\n    tx.Rollback()\n    return fmt.Errorf(\"create child: %w\", err) // retry whole tx\n}","handlingStrategy":"retry","validationCode":"if ctx.Err() != nil { return ctx.Err() }","typeGuard":null,"tryCatchPattern":"id, err := issueops.GetNextChildIDTx(ctx, tx, parentID)\nif err != nil {\n    tx.Rollback()\n    return retry(fmt.Errorf(\"counter update failed: %w\", err)) // safe: idempotent recompute\n}","preventionTips":["Serialize sub-issue creation under the same parent (or accept retries).","Keep transactions short to avoid counter-row lock waits.","Ensure schema migrations create the counter table before use.","Treat this error as retryable: recomputing the child ID is deterministic."],"tags":["database","sql","counter","concurrency"],"backgroundTag":"sql-exec-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}