{"record":{"id":"7357cf84106eb82a","repo":"gastownhall/beads","slug":"w-bootstrap-requires-a-project-id-this-role-doe","errorCode":null,"errorMessage":"%w: bootstrap requires a project id; this role does not mint one","messagePattern":"%w: bootstrap requires a project id; this role does not mint one","errorType":"validation","errorClass":"issueops.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/workapi/bootstrap.go","lineNumber":56,"sourceCode":"// VerifyIdentity answers with are one value on every backend, including the\n// unit-of-work one whose write does not pass through that plane's normalizer.\nfunc NormalizeBootstrapPrefix(prefix string) string {\n\treturn strings.TrimRight(prefix, \"-\")\n}\n\n// ValidateBootstrapRequest refuses a bootstrap that cannot produce a usable\n// workspace and returns the request as it will be WRITTEN.\n//\n// Every implementation validates through it BEFORE opening a transaction, which\n// is what makes issueops.Bootstrapper's \"a refusal writes nothing\" true of the\n// connection as well as of the keys.\nfunc ValidateBootstrapRequest(req issueops.BootstrapRequest) (issueops.BootstrapRequest, error) {\n\treq.Prefix = NormalizeBootstrapPrefix(req.Prefix)\n\tif req.Prefix == \"\" {\n\t\treturn issueops.BootstrapRequest{}, fmt.Errorf(\"%w: bootstrap requires a non-empty issue prefix\", issueops.ErrValidation)\n\t}\n\tif req.ProjectID == \"\" {\n\t\treturn issueops.BootstrapRequest{}, fmt.Errorf(\"%w: bootstrap requires a project id; this role does not mint one\", issueops.ErrValidation)\n\t}\n\treturn req, nil\n}\n\n// RefuseIdentifiedSubstrate turns the pair a bootstrap read into the refusal\n// issueops.Bootstrapper promises, or nil when the substrate is free.\n//\n// EITHER MARKER IS ENOUGH. A substrate several rigs share carries a prefix one\n// of them chose, and a bootstrap that overwrote it would rename every id the\n// others are about to mint; a substrate carrying only a project id is either a\n// server-provisioned one bd must adopt rather than stamp, or a bootstrap that\n// failed partway, and bd cannot tell those apart. The error carries both values\n// so its caller can.\nfunc RefuseIdentifiedSubstrate(prefix, projectID string) error {\n\tif prefix == \"\" && projectID == \"\" {\n\t\treturn nil\n\t}\n\treturn &issueops.AlreadyIdentifiedError{Prefix: prefix, ProjectID: projectID}","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/workapi/bootstrap.go#L38-L74","documentation":"ValidateBootstrapRequest wraps issueops.ErrValidation when ProjectID is empty. The bootstrap role expects the project (or equivalent substrate identifier) to already exist and be supplied by the caller; it does not mint one itself. Like the prefix check, this fires before any transaction opens.","triggerScenarios":"Calling an issueops Bootstrapper with BootstrapRequest{ProjectID: \"\"}; the ProjectID field was never populated from config or a prior substrate-creation step.","commonSituations":"Forgetting to create/fetch the project before bootstrapping; passing a struct literal with only Prefix set; config wiring that skips the project id key.","solutions":["Create or look up the project first, then pass its id in BootstrapRequest.ProjectID","Verify the config/env wiring actually populates ProjectID before calling bootstrap","If you expected the library to create the project, note this role intentionally refuses to mint one — do that step in your setup code"],"exampleFix":"// before\nreq := issueops.BootstrapRequest{Prefix: \"bd\"} // ProjectID missing\n// after\nprojectID := ensureProjectExists(ctx, client)\nreq := issueops.BootstrapRequest{Prefix: \"bd\", ProjectID: projectID}","handlingStrategy":"validation","validationCode":"if req.ProjectID == \"\" {\n    return fmt.Errorf(\"project must be created/selected before bootstrap\")\n}","typeGuard":null,"tryCatchPattern":"req, err := workapi.ValidateBootstrapRequest(req)\nif errors.Is(err, issueops.ErrValidation) && strings.Contains(err.Error(), \"project id\") {\n    projectID, lookupErr := ensureProject(ctx)\n    if lookupErr != nil { return lookupErr }\n    return retryWithProjectID(projectID)\n}","preventionTips":["Create the project/substrate as an explicit setup step before bootstrap","Store and reuse the project id in config after first creation","Never assume bootstrap mints project ids — this role refuses to"],"tags":["validation","bootstrap","project-id","issueops"],"backgroundTag":"bootstrap-validation-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}