{"record":{"id":"78d0f2455a0cd3bb","repo":"gastownhall/beads","slug":"check-rename-collision-in-s-w","errorCode":null,"errorMessage":"check rename collision in %s: %w","messagePattern":"check rename collision in (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":898,"sourceCode":"\n\tquery := fmt.Sprintf(`\n\t\tSELECT 1 FROM %s a\n\t\tJOIN %s b ON a.issue_id = b.issue_id\n\t\tWHERE a.%s = ?\n\t\t  AND (b.%s = ? OR b.%s = ?)\n\t\tLIMIT 1\n\t`, table, table, typedCol, otherCols[0], otherCols[1])\n\n\tvar found int\n\terr := tx.QueryRowContext(ctx, query, newID, newID, newID).Scan(&found)\n\tif err == sql.ErrNoRows {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\tif isTableNotExistError(err) {\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"check rename collision in %s: %w\", table, err)\n\t}\n\treturn fmt.Errorf(\"rename to %s collides with existing dependency target in %s\", newID, table)\n}\n\n// RemoveDependencyInTx removes a dependency between two issues within an\n// existing transaction. Automatically routes to wisp_dependencies if the\n// source issue is an active wisp. When emitEvent is set and a row is actually\n// removed it records a dependency_removed event (attributed to actor) on the\n// source's event table; a no-op remove of a missing edge, or a structural remove\n// with emitEvent unset, records nothing. Only the explicit bd dep remove verb\n// sets emitEvent; structural removals (issue delete, reparent, batch, duplicate\n// cleanup) leave it unset so they wire edges away silently, mirroring the\n// proxied repository's DepInsertOpts.EmitEvent gate so both backends record\n// identical history.\n//\n// It returns whether a dependency_removed event was actually written, so callers\n// that stage tables for a Dolt commit stage the events table only when an event\n// row exists (avoiding the sweep-unrelated-rows hazard doltAddAndCommit guards","sourceCodeStart":880,"sourceCodeEnd":916,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L880-L916","documentation":"Wrapped SQL failure from checkRenameTargetCollision: the probe that checks whether renaming a dependency target to newID would conflict with another target column on the same issue_id failed at the driver level. Unlike the deliberate collision error (3545), the query itself errored rather than finding a row. Table-not-exist is tolerated as no-op.","triggerScenarios":"Any caller (replaceDependencyTargetInTx, RetargetInboundDependenciesToWispInTx, RetargetInboundDependenciesToIssueInTx, UpdateIssueIDInDependencyTargetsInTx) runs the JOIN probe against dependencies or wisp_dependencies and gets a non-ErrNoRows driver error — connectivity loss, timeout, schema corruption, permission denial.","commonSituations":"Renaming an issue ID (bd mv / update --id) while the DB connection is flaky; Dolt server restart mid-rename; insufficient SELECT grants; a partially migrated schema missing typed target columns.","solutions":["Read the wrapped driver error; retry the rename transaction if it is transient (connection, timeout, lock).","Verify schema completeness with migrations; both dependency tables need depends_on_issue_id, depends_on_wisp_id, depends_on_external.","Confirm the DB user can SELECT from both dependency tables.","Check server logs (Dolt/SQLite) for the underlying query failure."],"exampleFix":"// before: rename fails on flaky connection\nerr := store.RenameIssue(ctx, oldID, newID)\n// after: retry rename with backoff on transient DB errors\nerr = withRetry(3, func() error { return store.RenameIssue(ctx, oldID, newID) })","handlingStrategy":"retry","validationCode":"// verify rename-probe prerequisites: table readable and expected columns present\nfor _, c := range []string{\"depends_on_issue_id\", \"depends_on_wisp_id\", \"depends_on_external\"} {\n    if err := db.QueryRow(\"SELECT \" + c + \" FROM dependencies LIMIT 1\").Err(); err != nil {\n        return fmt.Errorf(\"dependencies.%s unavailable; migrate first: %w\", c, err)\n    }\n}","typeGuard":"func isRenameProbeFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"check rename collision\")\n}","tryCatchPattern":"err := store.RenameIssue(ctx, oldID, newID)\nif isRenameProbeFailure(err) && isTransient(errors.Unwrap(err)) {\n    err = withBackoff(3, func() error { return store.RenameIssue(ctx, oldID, newID) })\n}","preventionTips":["Retry whole rename transactions on transient driver errors.","Keep schema migrations current so all three target columns exist.","Set busy_timeout/lock waits appropriate to your concurrency level.","Watch DB server logs for probe-query failures during rename windows."],"tags":["storage","sql","dependencies","rename"],"backgroundTag":"dependency-collision","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}