gastownhall/beads · error
node %q: storage_class ephemeral and no_history are mutually
Error message
node %q: storage_class ephemeral and no_history are mutually exclusive
What it means
If a node resolves to storage class `ephemeral`, it is placed on the wisp plane, which by definition has no durable history. Requesting no_history alongside that is redundant-conflicting, so validation rejects it. This is the storage_class-based path of the same exclusivity rule as error 710.
Source
Thrown at cmd/bd/graph_apply.go:908
}
noHistory = opts.NoHistory
if node.NoHistory != nil {
noHistory = *node.NoHistory
}
if ephemeral && noHistory {
return false, false, "", fmt.Errorf("node %q: ephemeral and no_history are mutually exclusive", node.Key)
}
issueType := types.IssueType(node.Type)
if issueType == "" {
issueType = types.TypeTask
}
class, err = resolveStorageClass(node.StorageClass, issueType.Normalize())
if err != nil {
return false, false, "", fmt.Errorf("node %q: %w", node.Key, err)
}
if class == types.StorageClassEphemeral {
if noHistory {
return false, false, "", fmt.Errorf("node %q: storage_class ephemeral and no_history are mutually exclusive", node.Key)
}
if node.Ephemeral != nil && !*node.Ephemeral {
return false, false, "", fmt.Errorf("node %q: storage_class ephemeral conflicts with ephemeral: false", node.Key)
}
ephemeral = true
class = ""
}
// Reconcile the requested durable class with the effective wisp plane
// (flag > config, Protocol v0.1 §C1.3): an explicit node storage_class
// contradicts an ephemeral/no_history node and is rejected, so the durable
// intent is preserved rather than silently collapsed into an
// effective-ephemeral record; a per-type config default yields to the
// effective plane. versioned normalizes to the unset marker only after the
// check (C2.4).
var conflict bool
class, conflict = reconcileStorageClassPlane(class, node.StorageClass != "", ephemeral || noHistory)
if conflict {
return false, false, "", fmt.Errorf("node %q: storage_class %s conflicts with ephemeral/no_history: wisp-plane records are storage class ephemeral", node.Key, class)View on GitHub (pinned to 71377f2769)
Solutions
- Drop `no_history` from the node (or unset the --no-history option) since storage_class ephemeral already implies it.
- Change storage_class to a non-ephemeral value if you need no_history with durable storage.
- Review type-default storage_class configuration that may implicitly make nodes ephemeral.
Example fix
// before
{"key": "bd-123", "storage_class": "ephemeral", "no_history": true}
// after
{"key": "bd-123", "storage_class": "ephemeral"} Defensive patterns
Strategy: validation
Validate before calling
for _, n := range plan.Nodes {
if n.StorageClass == "ephemeral" && n.NoHistory != nil && *n.NoHistory {
return fmt.Errorf("node %q: drop no_history when storage_class is ephemeral", n.Key)
}
} Type guard
func ephemeralWithNoHistory(n Node) bool {
return n.StorageClass == "ephemeral" && n.NoHistory != nil && *n.NoHistory
} Try / catch
if err := bd.GraphApply(ctx, plan); err != nil {
if strings.Contains(err.Error(), "mutually exclusive") {
plan = stripNoHistoryFromEphemeralNodes(plan); return bd.GraphApply(ctx, plan)
}
} Prevention
- Treat storage_class ephemeral as implying no_history; never set both.
- Avoid global --no-history when per-node storage_class defaults to ephemeral.
- Lint plans for redundant retention flags.
When it happens
Trigger: Applying a graph plan where a node has `storage_class: "ephemeral"` and `no_history: true`, or inherits ephemeral storage class while the no_history flag/option is set.
Common situations: Combining per-node storage_class with global --no-history; type-default configs mapping a type to ephemeral while the plan sets no_history; copy-pasted retention settings.
Related errors
- node %q: ephemeral and no_history are mutually exclusive
- node %q: storage_class ephemeral conflicts with ephemeral: f
- node %q: storage_class %s conflicts with ephemeral/no_histor
- edge %d %s->%s duplicates a parent-child relationship with d
- applyGraph: node %q storage class (ephemeral=%t, no_history=
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/5abfe4fe5e6e36c6.
Report an issue: GitHub.