gastownhall/beads · error
provenance: issue id is required
Error message
provenance: issue id is required
What it means
ValidateProvenanceEvent enforces the structural rules for a types.ProvenanceEvent before it is stored. A ProvenanceEvent with an empty or whitespace-only IssueID is rejected because every provenance event must be attributed to an issue. The same validation runs in RecordProvenanceEventInTx and is exported so the CLI can fail early with identical rules.
Source
Thrown at internal/storage/issueops/provenance.go:52
"work-id": {},
"transcript": {},
"branch": {},
}
// ReservedProvSource is reserved for derived/reconstructed events so a consumer's
// read-first honesty filter can exclude backfilled rows. The record path rejects
// it (case-insensitively): real producers must name their own source.
const ReservedProvSource = "ingest-backfill"
var gitSHARE = regexp.MustCompile(`^[0-9a-f]{40}$`)
// ValidateProvenanceEvent checks the structural fields of a provenance event
// before it is recorded: kind, ref_kind (when present), the git-sha ref shape,
// and the reserved source. It never interprets the opaque actor/ref values. It
// is exported so the CLI can fail early with the same rules the store enforces.
func ValidateProvenanceEvent(ev types.ProvenanceEvent) error {
if strings.TrimSpace(ev.IssueID) == "" {
return fmt.Errorf("provenance: issue id is required")
}
if _, ok := knownProvKinds[ev.Kind]; !ok {
return fmt.Errorf("provenance: unknown kind %q", ev.Kind)
}
if strings.TrimSpace(ev.Source) == "" {
return fmt.Errorf("provenance: source is required")
}
if strings.EqualFold(strings.TrimSpace(ev.Source), ReservedProvSource) {
return fmt.Errorf("provenance: source %q is reserved for ingest backfill and cannot be recorded directly", ReservedProvSource)
}
if ev.RefKind != nil {
if _, ok := knownProvRefKinds[*ev.RefKind]; !ok {
return fmt.Errorf("provenance: unknown ref-kind %q", *ev.RefKind)
}
if ev.Ref == nil || *ev.Ref == "" {
return fmt.Errorf("provenance: ref-kind %q requires a ref", *ev.RefKind)
}
if *ev.RefKind == "git-sha" {View on GitHub (pinned to 71377f2769)
Solutions
- Populate ev.IssueID with the real issue ID before validation/recording
- Call bd create / the create API first and use its returned ID; never record provenance for an un-created issue
- Trim and check the field in your own code before constructing the event
- For batch imports, skip or queue events whose issue ID is unknown and log them instead of failing the batch
Example fix
// before
_ = issueops.RecordProvenanceEventInTx(ctx, tx, types.ProvenanceEvent{Kind: types.ProvClaim, Source: "agent"})
// after
ev := types.ProvenanceEvent{IssueID: issue.ID, Kind: types.ProvClaim, Source: "agent"}
if err := issueops.ValidateProvenanceEvent(ev); err != nil {
return fmt.Errorf("invalid provenance event: %w", err)
}
return issueops.RecordProvenanceEventInTx(ctx, tx, ev) Defensive patterns
Strategy: validation
Validate before calling
func validProvEvent(ev types.ProvenanceEvent) error {
if strings.TrimSpace(ev.IssueID) == "" { return errors.New("provenance: issue id is required") }
return issueops.ValidateProvenanceEvent(ev)
} Type guard
func hasIssueID(ev types.ProvenanceEvent) bool { return strings.TrimSpace(ev.IssueID) != "" } Try / catch
if err := issueops.RecordProvenanceEventInTx(ctx, tx, ev); err != nil {
if strings.Contains(err.Error(), "issue id is required") {
return fmt.Errorf("cannot record provenance: event not bound to an issue (id=%q)", ev.IssueID)
}
return err
} Prevention
- Construct events only after the issue ID is known (post-create)
- Run ValidateProvenanceEvent before opening any transaction
- Use a constructor helper that requires issueID so it cannot be omitted
- Trim IDs — whitespace-only values fail the same check
When it happens
Trigger: Recording a provenance event via RecordProvenanceEventInTx (or pre-validating via ValidateProvenanceEvent) with ev.IssueID == "" or only whitespace (e.g. " ").
Common situations: Building events programmatically where the issue ID variable was never populated; parsing CLI output where an empty field became an empty string; calling RecordProvenanceEvent before the issue ID has been assigned (e.g. before create returns the ID).
Related errors
- provenance: unknown kind %q
- provenance: source is required
- provenance: source %q is reserved for ingest backfill and ca
- provenance: unknown ref-kind %q
- no store is open for this workspace
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3739566955a1f709.
Report an issue: GitHub.