gastownhall/beads · error
cli version must not be empty
Error message
cli version must not be empty
What it means
ValidateReconcileVersion refuses an empty or whitespace-only cliVersion. Recording "" over a real marker would silently break the downgrade guard, and the empty marker it leaves behind is indistinguishable from a workspace nothing has ever reconciled. The only rule at this end is that a version has to name something.
Source
Thrown at internal/workapi/versionreconcile.go:60
// together: a workspace catching up to a mark a newer binary already left
// moves the marker to the mark and leaves the mark where it is.
RecordHighWaterMark bool
}
// ValidateReconcileVersion checks the version a caller wants to record, and
// returns it unchanged.
//
// It is separate from the planner so a body can refuse BEFORE it reads anything
// or opens a unit of work. This runs on every startup, so a refusal that costs
// a round trip is a refusal that costs every command.
//
// The only rule at this end is that a version has to name something. Recording
// "" over a real marker would silently take the downgrade guard down with it,
// and the empty marker it left behind is indistinguishable from a workspace
// nothing has ever reconciled.
func ValidateReconcileVersion(cliVersion string) (string, error) {
if strings.TrimSpace(cliVersion) == "" {
return "", fmt.Errorf("%w: cli version must not be empty", issueops.ErrValidation)
}
return cliVersion, nil
}
// PlanVersionReconcile decides what recording cliVersion should do to a
// workspace whose markers currently read recorded and highWaterMark.
//
// The order of the checks is the contract, not an implementation detail. An
// exact match short-circuits first, so the steady-state path — every startup
// that is not the first one after an upgrade — decides without consulting the
// mark at all. Both downgrade guards then run BEFORE any write is planned, so
// a refusal is a decision rather than a write that is undone.
func PlanVersionReconcile(cliVersion, recorded, highWaterMark string) (VersionReconcilePlan, error) {
if _, err := ValidateReconcileVersion(cliVersion); err != nil {
return VersionReconcilePlan{}, err
}
if recorded == cliVersion {View on GitHub (pinned to 71377f2769)
Solutions
- Set cliVersion to the actual binary/build version string before calling PlanVersionReconcile
- Fix the build pipeline so the version ldflag is injected (e.g. -X main.version=...)
- Handle errors.Is(err, issueops.ErrValidation) and fail fast with 'cli version must not be empty'
Example fix
// before
plan, err := workapi.PlanVersionReconcile(version) // version == ""
// after
if strings.TrimSpace(version) == "" {
return fmt.Errorf("refusing reconcile: binary built without a version")
}
plan, err := workapi.PlanVersionReconcile(version) Defensive patterns
Strategy: validation
Validate before calling
if strings.TrimSpace(cliVersion) == "" {
return fmt.Errorf("cli version must not be empty before reconcile")
} Type guard
func hasVersion(v string) bool {
return strings.TrimSpace(v) != ""
} Try / catch
plan, err := workapi.PlanVersionReconcile(cliVersion)
if err != nil {
if errors.Is(err, issueops.ErrValidation) {
return fmt.Errorf("cannot reconcile: %w", err)
}
return err
} Prevention
- Inject the version at build time via ldflags and fail the build if it is empty
- Never pass env/config-sourced version strings without an emptiness check
- Fail fast at startup when the binary reports an empty version, before touching workspace markers
When it happens
Trigger: Calling workapi.ValidateReconcileVersion("") directly, or calling PlanVersionReconcile, which invokes it, with a version string that is empty or whitespace.
Common situations: A build-time ldflags version variable was not injected (binary built without the version flag); a caller read the version from an unset env/config value; a test harness forgot to set the version before planning the reconcile.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- no store is open for this workspace
- not found
- no absolute native user directory is available
- ExternalDoltConfig: set either Socket OR (Host, Port), not b
- ExternalDoltConfig: must set Socket or (Host, Port)
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/8d0a842a43e04ba8.
Report an issue: GitHub.