gastownhall/beads · error
DeleteWisp: id must not be empty
Error message
DeleteWisp: id must not be empty
What it means
DeleteWisp validates its required id argument before doing any work and returns this error when the id is an empty string. Like DeleteIssue, it is an input-validation guard at the use-case layer; no storage call occurs. It applies to wisp (ephemeral issue) deletion.
Source
Thrown at internal/storage/domain/issue_delete.go:42
func (e *DeleteBlockedError) Error() string {
return fmt.Sprintf("issue %s has dependents not in deletion set; use --cascade to delete them or --force to orphan them", e.IssueID)
}
func (u *issueUseCaseImpl) DeleteIssue(ctx context.Context, id, actor string) (DeleteIssuesResult, error) {
if id == "" {
return DeleteIssuesResult{}, fmt.Errorf("DeleteIssue: id must not be empty")
}
return u.deleteMany(ctx, DeleteIssuesParams{
IDs: []string{id},
Cascade: true,
UpdateTextReferences: true,
}, actor)
}
func (u *issueUseCaseImpl) DeleteWisp(ctx context.Context, id, actor string) (DeleteIssuesResult, error) {
if id == "" {
return DeleteIssuesResult{}, fmt.Errorf("DeleteWisp: id must not be empty")
}
return u.deleteMany(ctx, DeleteIssuesParams{
IDs: []string{id},
Cascade: true,
UpdateTextReferences: true,
}, actor)
}
func (u *issueUseCaseImpl) DeleteIssues(ctx context.Context, params DeleteIssuesParams, actor string) (DeleteIssuesResult, error) {
return u.deleteMany(ctx, params, actor)
}
func (u *issueUseCaseImpl) DeleteWisps(ctx context.Context, params DeleteIssuesParams, actor string) (DeleteIssuesResult, error) {
return u.deleteMany(ctx, params, actor)
}
func (u *issueUseCaseImpl) PreviewDelete(ctx context.Context, ids []string) (DeletePreview, error) {
return u.previewDelete(ctx, ids)View on GitHub (pinned to 71377f2769)
Solutions
- Pass the real wisp ID to DeleteWisp
- Validate the wisp ID exists at the source (lookup result, JSON field) before calling
- Skip empty entries in batch-delete loops instead of passing them through
Example fix
// before
for _, w := range wisps {
uc.DeleteWisp(ctx, w.ID, actor) // panics the flow with empty-id error
}
// after
for _, w := range wisps {
if w.ID == "" { continue }
uc.DeleteWisp(ctx, w.ID, actor)
} Defensive patterns
Strategy: validation
Validate before calling
for _, w := range wisps {
if strings.TrimSpace(w.ID) == "" { continue } // skip malformed entries
if _, err := uc.DeleteWisp(ctx, w.ID, actor); err != nil { return err }
} Type guard
func hasWispID(w Wisp) bool { return strings.TrimSpace(w.ID) != "" } Try / catch
res, err := uc.DeleteWisp(ctx, id, actor)
if err != nil && strings.Contains(err.Error(), "DeleteWisp: id must not be empty") {
log.Printf("skipping: empty wisp id")
return nil
} Prevention
- Filter out entries with missing IDs before batch wisp deletion
- Validate JSONL/export records before loading them into delete flows
- Check lookup results for success before reusing the returned ID
When it happens
Trigger: Calling DeleteWisp(ctx, "", actor) — an uninitialized wisp ID, a failed wisp lookup returning an empty ID, or whitespace-only input that was trimmed to "".
Common situations: Agent loops deleting wisps from a batch where some entries lack IDs; reading wisp IDs from JSONL export files with missing fields; variable shadowing leaving the ID unset.
Related errors
- remove dep: sourceID and dependsOnID must not be empty
- reparent: childID must not be empty
- DeleteIssue: id must not be empty
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/06282d2724073a4b.
Report an issue: GitHub.