{"record":{"id":"5f35b678c74d875c","repo":"gastownhall/beads","slug":"w-w-5f35b6","errorCode":null,"errorMessage":"%w: %w","messagePattern":"%w: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/public_create.go","lineNumber":100,"sourceCode":"\t\tprepared.WaitsFor.Gate = string(types.WaitsForAllChildren)\n\t}\n\tif err := ValidatePublicCreateRequest(prepared); err != nil {\n\t\treturn publicops.CreateRequest{}, err\n\t}\n\treturn prepared, nil\n}\n\n// ClassifyPublicCreateError adds ErrValidation only to known deterministic\n// public-create failures and leaves infrastructure and commit errors intact.\nfunc ClassifyPublicCreateError(err error) error {\n\tif err == nil || errors.Is(err, storage.ErrValidation) || errors.Is(err, storage.ErrAlreadyExists) {\n\t\treturn err\n\t}\n\tvar conflict *domain.DependencyTypeConflictError\n\tvar hierarchyConflict *domain.DependencyHierarchyConflictError\n\tvar stateErr interface{ SQLState() string }\n\tif errors.As(err, &stateErr) && stateErr.SQLState() == \"23505\" {\n\t\treturn fmt.Errorf(\"%w: %w\", storage.ErrAlreadyExists, err)\n\t}\n\tif errors.Is(err, storage.ErrPrefixMismatch) || errors.Is(err, domain.ErrSelfDependency) || errors.Is(err, types.ErrFieldTooLong) || errors.Is(err, domain.ErrDependencyCycle) || errors.As(err, &conflict) || errors.As(err, &hierarchyConflict) {\n\t\treturn publicCreateValidationError(err)\n\t}\n\t// A create whose requested relationship names a row that does not exist is\n\t// refused by the dependency write: as the typed endpoint refusal where the\n\t// write could name the absent endpoint, and as the target foreign key where\n\t// it could not. The caller asked for an edge to something absent, so this\n\t// is a deterministic refusal rather than an infrastructure error: classify\n\t// it the same way ExecuteCreate refuses a skipped dependency, so every\n\t// backend reports a missing dependency, parent, or waits-for target as\n\t// ErrValidation wrapping ErrNotFound.\n\tvar missingEndpoint *domain.DependencyEndpointNotFoundError\n\tif errors.As(err, &missingEndpoint) || dberrors.IsMissingForeignKeyTarget(err) {\n\t\treturn publicCreateValidationError(fmt.Errorf(\"create: dependency target does not exist: %w: %w\", err, storage.ErrNotFound))\n\t}\n\treturn err\n}","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/public_create.go#L82-L118","documentation":"ClassifyPublicCreateError maps low-level persistence errors from a create batch onto typed public errors. A SQLState 23505 (unique violation) is re-wrapped as storage.ErrAlreadyExists together with the underlying error, so callers can detect duplicates with errors.Is while retaining the root cause.","triggerScenarios":"Calling ExecuteCreate/ExecuteCreateBatch when the insert violates a unique constraint — most often creating an issue whose ID already exists, or a duplicate row in a batch with identical deterministic identities.","commonSituations":"Re-running a batch import without idempotency handling; two issues in one batch that normalize to the same identity; retry after a partial failure where the first attempt already committed.","solutions":["Handle the duplicate: check errors.Is(err, storage.ErrAlreadyExists) and treat the create as already-done if the existing row matches","Make batch creates idempotent — deduplicate IDs/identities in the batch before calling ExecuteCreateBatch","Unwrap the inner error (%w: %w) to see which constraint/index was violated and adjust the payload"],"exampleFix":"// before\n_, err := ExecuteCreate(ctx, tx, req)\nif err != nil { return err }\n// after\n_, err := ExecuteCreate(ctx, tx, req)\nif err != nil {\n    if errors.Is(err, storage.ErrAlreadyExists) {\n        return nil // already created; idempotent no-op\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"seen := map[string]bool{}\nfor _, r := range batch {\n  if seen[r.Issue.ID] { return errors.New(\"duplicate id in batch: \" + r.Issue.ID) }\n  seen[r.Issue.ID] = true\n}","typeGuard":null,"tryCatchPattern":"_, err := ExecuteCreateBatch(ctx, tx, batch)\nif err != nil {\n  if errors.Is(err, storage.ErrAlreadyExists) {\n    // duplicate: log and continue idempotently\n    return nil\n  }\n  return err\n}","preventionTips":["Deduplicate issue IDs/identities before batch creation","Make create flows idempotent: treat ErrAlreadyExists as success when content matches","On retry after partial failure, check existence first or upsert where supported"],"tags":["create","duplicate","unique-constraint","storage"],"backgroundTag":"already-exists","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}