gastownhall/beads · error

provenance: unknown ref-kind %q

Error message

provenance: unknown ref-kind %q

What it means

If an event carries a RefKind, it must be one of the known ref kinds (git-sha, pr, work-id, transcript, branch); an unrecognized RefKind is rejected with 'provenance: unknown ref-kind %q'. RefKind is a closed vocabulary so downstream tools know how to resolve refs.

Source

Thrown at internal/storage/issueops/provenance.go:65

// 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" {
			if !gitSHARE.MatchString(*ev.Ref) {
				return fmt.Errorf("provenance: ref-kind git-sha requires a 40-character lowercase hex ref")
			}
		}
	}
	// A ref-less event is keyed by occurred_at for its stable id; without either,
	// two distinct events would collapse to the same content-addressed id. Guard
	// at the store boundary so every caller (CLI or library) is covered.
	if (ev.Ref == nil || *ev.Ref == "") && ev.OccurredAt == nil {
		return fmt.Errorf("provenance: event with no ref requires occurred_at (--at) for a stable id")
	}
	return nil
}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Translate external link types to the supported set: commit SHA -> "git-sha", PR number -> "pr", etc.
  2. Only set ev.RefKind when you actually have a ref; leave it nil otherwise (note the next check also demands a non-empty Ref for any set RefKind)
  3. Use string constants mirroring knownProvRefKinds rather than inline literals
  4. Pre-validate with ValidateProvenanceEvent before opening the transaction

Example fix

// before
evk := "commit"
ev := types.ProvenanceEvent{IssueID: id, Kind: types.ProvCommit, Source: "ci", RefKind: &evk, Ref: &sha}
// after
evk := "git-sha" // must be one of: git-sha, pr, work-id, transcript, branch
ev := types.ProvenanceEvent{IssueID: id, Kind: types.ProvCommit, Source: "ci", RefKind: &evk, Ref: &sha}
if err := issueops.ValidateProvenanceEvent(ev); err != nil {
	return err
}
Defensive patterns

Strategy: validation

Validate before calling

validRefKinds := map[string]bool{"git-sha": true, "pr": true, "work-id": true, "transcript": true, "branch": true}
if ev.RefKind != nil && !validRefKinds[*ev.RefKind] {
	return fmt.Errorf("unsupported ref-kind %q", *ev.RefKind)
}
return issueops.ValidateProvenanceEvent(ev)

Type guard

func isKnownRefKind(k *string) bool {
	if k == nil { return true }
	switch *k {
	case "git-sha", "pr", "work-id", "transcript", "branch": return true
	}
	return false
}

Try / catch

if err := issueops.ValidateProvenanceEvent(ev); err != nil {
	if strings.Contains(err.Error(), "unknown ref-kind") {
		return fmt.Errorf("translate ref-kind %q to git-sha/pr/work-id/transcript/branch", *ev.RefKind)
	}
	return err
}

Prevention

When it happens

Trigger: Recording an event with ev.RefKind set to anything outside {"git-sha","pr","work-id","transcript","branch"} — e.g. "commit", "sha", "url", or the empty string in a non-nil pointer.

Common situations: Mapping external systems' link types (commit, issue URL) directly onto RefKind without translating to the supported set; typo'd ref kinds in scripts; nil-vs-empty confusion producing RefKind="".

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/59a5f0e4e1467299. Report an issue: GitHub.