gastownhall/beads · error
wisp %s not found
Error message
wisp %s not found
What it means
PromoteFromEphemeralInTx first checks IsActiveWispInTx; if the ID is not currently an active wisp, it fails immediately with 'wisp %s not found'. Promotion only applies to live ephemeral (wisp-plane) issues, so any ID that is absent, already promoted, or fully deleted produces this error.
Source
Thrown at internal/storage/issueops/promote.go:14
package issueops
import (
"context"
"fmt"
"github.com/steveyegge/beads/internal/storage"
"github.com/steveyegge/beads/internal/types"
)
//nolint:gosec // G201: table names are hardcoded constants
func PromoteFromEphemeralInTx(ctx context.Context, tx DBTX, id string, actor string) error {
if !IsActiveWispInTx(ctx, tx, id) {
return fmt.Errorf("wisp %s not found", id)
}
issue, err := GetIssueInTx(ctx, tx, id)
if err != nil {
return fmt.Errorf("get wisp for promote: %w", err)
}
if issue == nil {
return fmt.Errorf("wisp %s not found", id)
}
// A promoted wisp is fully durable: clear BOTH wisp-plane flags, not just
// Ephemeral. A no-history wisp (Ephemeral=false, NoHistory=true) promoted
// with NoHistory intact lands in the issues table still flag-marked as
// wisp-plane state, and everything that infers the plane from flags —
// most damagingly import's table routing — silently re-planes it back
// into the (default-export-excluded) wisps table, dropping its relations
// on the way (bd-r9uce). Post-promotion the flag has no meaning.
issue.Ephemeral = falseView on GitHub (pinned to 71377f2769)
Solutions
- Check the issue's current plane/state before promoting (query the wisp table or use a read API) and skip already-promoted IDs
- Treat this as idempotent success if the ID now exists as a durable issue — the promote already happened
- Re-check the ID for typos or stale references; run bd list to find current wisp IDs
- If the wisp was created in another clone, sync first (bd dolt pull)
Example fix
// before
if err := issueops.PromoteFromEphemeralInTx(ctx, tx, id, actor); err != nil {
return err
}
// after
if err := issueops.PromoteFromEphemeralInTx(ctx, tx, id, actor); err != nil {
if strings.Contains(err.Error(), "not found") && issueExistsDurable(ctx, tx, id) {
return nil // already promoted; idempotent retry
}
return err
} Defensive patterns
Strategy: validation
Validate before calling
active, err := isActiveWisp(ctx, tx, id) // check the wisp plane first
if err != nil { return err }
if !active { return fmt.Errorf("skip %s: not an active wisp", id) } Type guard
func isActiveWisp(ctx context.Context, tx issueops.DBTX, id string) (bool, error) // plus: func isDurableIssue(ctx context.Context, tx issueops.DBTX, id string) (bool, error) // to distinguish 'already promoted' from 'never existed'
Try / catch
if err := issueops.PromoteFromEphemeralInTx(ctx, tx, id, actor); err != nil {
if isWispNotFound(err) {
if exists, _ := isDurableIssue(ctx, tx, id); exists {
return nil // idempotent: already promoted
}
return fmt.Errorf("wisp %s does not exist", id)
}
return err
} Prevention
- Make promote idempotent: treat already-durable as success
- Fetch wisp IDs from a live query, not cached lists
- Sync before batch promotions if wisps may originate in other clones
- Verify IDs against bd list output before scripting promotions
When it happens
Trigger: Calling PromoteFromEphemeralInTx with an ID that is not an active wisp: the issue does not exist, was already promoted to the durable plane, was deleted, or exists only as a durable (non-ephemeral) issue.
Common situations: Double-promotion after a retry that actually succeeded the first time; promoting an ID copied from the issues table instead of the wisp table; a stale script referencing wisps that were garbage-collected.
Related errors
- no store is open for this workspace
- determining wisp status for %s: %w
- failed to close wisp root %s: %w
- failed to clear ephemeral flag on root %s: %w
- remove dep: classify source: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/40dec38a543d5120.
Report an issue: GitHub.