gastownhall/beads · error

create: issue must not be nil

Error message

create: issue must not be nil

What it means

createParams rejects a CreateRequest whose Issue field is nil before doing any storage work. The request must carry the issue payload to create; a nil pointer would otherwise panic during cloning and parameter building. It is a validationError, so callers can distinguish bad input from storage failures.

Source

Thrown at internal/storage/uow/issue_operations.go:121

		if useWisp {
			created, err = uw.IssueUseCase().CreateWisp(ctx, params, attempt.Actor)
		} else {
			created, err = uw.IssueUseCase().CreateIssue(ctx, params, attempt.Actor)
		}
		if err != nil {
			return publicops.CreateResult{}, "", storageissueops.ClassifyPublicCreateError(err)
		}
		issue, err := hydrateIssueOperation(ctx, uw, created.Issue, true, false)
		if err != nil {
			return publicops.CreateResult{}, "", err
		}
		return publicops.CreateResult{Issue: issue}, "create issue", nil
	})
}

func createParams(request publicops.CreateRequest) (domain.CreateIssueParams, bool, error) {
	if request.Issue == nil {
		return domain.CreateIssueParams{}, false, fmt.Errorf("create: issue must not be nil")
	}
	issue := storageissueops.CloneCreateRequest(publicops.CreateRequest{Issue: request.Issue}).Issue
	params := domain.CreateIssueParams{
		Issue:                   issue,
		ExplicitID:              issue.ID,
		ParentID:                request.ParentID,
		Labels:                  append([]string(nil), issue.Labels...),
		InheritLabelsFromParent: request.InheritLabelsFromParent,
		ForcePrefix:             request.ForceIDPrefix,
		CreateOnly:              true,
	}
	for _, dependency := range request.Dependencies {
		params.Dependencies = append(params.Dependencies, domain.DependencySpec{
			Type:          dependency.Type,
			TargetID:      dependency.TargetID,
			SwapDirection: dependency.Reverse,
			Metadata:      dependency.Metadata,
			ThreadID:      dependency.ThreadID,

View on GitHub (pinned to 71377f2769)

Solutions

  1. Always populate CreateRequest.Issue with a non-nil *types.Issue.
  2. Check the struct is fully initialized after deserialization before dispatching.
  3. Add a nil check on Issue at the construction site if it can come from external input.

Example fix

// before
req := publicops.CreateRequest{} // Issue nil
// after
req := publicops.CreateRequest{Issue: &types.Issue{ID: id, Title: title, Status: "open"}}
Defensive patterns

Strategy: validation

Validate before calling

if req.Issue == nil { return errors.New("create: issue is required") }

Type guard

func validCreateRequest(r publicops.CreateRequest) bool { return r.Issue != nil }

Try / catch

if err != nil {
    // validationError family: fix the request, do not retry
    return fmt.Errorf("bad create request: %w", err)
}

Prevention

When it happens

Trigger: Calling Create (or batch-create paths applyCreate/createBatchItem) with publicops.CreateRequest{Issue: nil}.

Common situations: Zero-value CreateRequest passed by mistake; a builder that failed earlier and left Issue nil; JSON deserialization that produced a null issue object.

Related errors


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