gastownhall/beads · error
issue must not be nil
Error message
issue must not be nil
What it means
createIssue (and its exported wrapper CreateIssue) rejects a nil *types.Issue pointer before doing any work. This is a programmer-error guard: passing nil means no issue data exists to persist, so the library returns a clear error instead of panicking on a nil dereference.
Source
Thrown at internal/storage/dolt/issues.go:31
"go.opentelemetry.io/otel/metric"
"github.com/steveyegge/beads/internal/idgen"
"github.com/steveyegge/beads/internal/storage"
"github.com/steveyegge/beads/internal/storage/issueops"
"github.com/steveyegge/beads/internal/types"
)
// CreateIssue creates a new issue.
// Delegates SQL work to issueops; handles Dolt versioning for non-ephemeral issues.
func (s *DoltStore) CreateIssue(ctx context.Context, issue *types.Issue, actor string) error {
return s.withCircuitWrite(ctx, func(ctx context.Context) error {
return s.createIssue(ctx, issue, actor)
})
}
func (s *DoltStore) createIssue(ctx context.Context, issue *types.Issue, actor string) error {
if issue == nil {
return fmt.Errorf("issue must not be nil")
}
// Route to wisps table if ephemeral, no-history, wisp-typed, or infra type.
// A wisp_type is a claim of ephemerality: minted without the flag it lands
// in the issues plane where no TTL, GC, or purge tier owns it.
useWispsTable := issue.Ephemeral || issue.NoHistory || issue.WispType != "" || s.IsInfraTypeCtx(ctx, issue.IssueType)
if useWispsTable && !issue.NoHistory {
issue.Ephemeral = true // infra and wisp types get marked ephemeral (legacy behavior)
}
var result issueops.CreateIssueResult
if err := s.withRetryTx(ctx, func(tx *sql.Tx) error {
// SkipPrefixValidation matches legacy behavior: single-issue path does
// not validate prefixes for explicit IDs.
bc, err := issueops.NewBatchContext(ctx, tx, storage.BatchCreateOptions{
SkipPrefixValidation: true,
})
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Ensure a non-nil *types.Issue with required fields (Title, etc.) is constructed before calling CreateIssue.
- Check the error return of whatever produced the issue pointer — a nil issue usually indicates an earlier ignored error.
- In wrappers/CLIs, validate decoded input and return a user-facing 'issue data required' message instead of passing nil through.
Example fix
// before
var issue *types.Issue // nil unless populated
json.Unmarshal(body, &issue) // may leave issue nil on empty body
store.CreateIssue(ctx, issue, actor) // "issue must not be nil"
// after
issue := &types.Issue{}
if err := json.Unmarshal(body, issue); err != nil { return err }
if issue.Title == "" { return fmt.Errorf("title required") }
store.CreateIssue(ctx, issue, actor) Defensive patterns
Strategy: type-guard
Validate before calling
if issue == nil || issue.Title == "" { return fmt.Errorf("issue with non-empty title required") }
store.CreateIssue(ctx, issue, actor) Type guard
func hasIssue(i *types.Issue) bool { return i != nil } Try / catch
if err := store.CreateIssue(ctx, issue, actor); err != nil {
if strings.Contains(err.Error(), "issue must not be nil") {
return fmt.Errorf("no issue data provided (check upstream error handling)")
}
return err
} Prevention
- Always check the error return of functions that produce *types.Issue before using the pointer.
- Construct issues with struct literals, not nil var declarations.
- Validate required fields (Title, IssueType) before calling create APIs.
When it happens
Trigger: Calling store.CreateIssue(ctx, nil, actor) — e.g. a variable that failed to initialize, a function that returns (issue, err) where issue is nil on an ignored error path, or decoding empty input into a nil pointer.
Common situations: JSON payloads that deserialize to a nil issue when a required field check is skipped; refactored code paths where an earlier error was swallowed and the zero-value (nil) pointer flowed onward; test harnesses passing nil placeholders.
Related errors
- add dep: dep must not be nil
- proxy.ForceStopUnverified: at most one options value is allo
- nil entry
- proxy.ForceStopUnverified: timeout must be positive, got %s
- add dep: IssueID and DependsOnID must be non-empty
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c595af7040ebe60e.
Report an issue: GitHub.