gastownhall/beads · error · storage.ErrValidation
%w: update estimated_minutes: %w
Error message
%w: update estimated_minutes: %w
What it means
This error wraps types.ValidateIssueEstimatedMinutes failures when a patch sets EstimatedMinutes. The aggregate validator rejects invalid estimates (e.g. negative or over-limit values) before the row is updated, and the error chain carries both storage.ErrValidation and the underlying cause.
Source
Thrown at internal/storage/issueops/aggregate.go:92
return fmt.Errorf("%w: claim cannot use expected assignee or status", storage.ErrValidation)
}
if request.ForceAssigneeTransfer && (request.Claim || !request.Patch.Assignee.Set || request.ExpectedAssignee != nil) {
return fmt.Errorf("%w: invalid forced assignee transfer", storage.ErrValidation)
}
patch := request.Patch
if patch.Title.Set {
if err := types.ValidateIssueTitle(patch.Title.Value); err != nil {
return fmt.Errorf("%w: update title: %w", storage.ErrValidation, err)
}
}
if patch.Priority.Set {
if err := types.ValidateIssuePriority(patch.Priority.Value); err != nil {
return fmt.Errorf("%w: update priority: %w", storage.ErrValidation, err)
}
}
if patch.EstimatedMinutes.Set {
if err := types.ValidateIssueEstimatedMinutes(patch.EstimatedMinutes.Value); err != nil {
return fmt.Errorf("%w: update estimated_minutes: %w", storage.ErrValidation, err)
}
}
if patch.Persistence.Set && !patch.Persistence.Value.IsValid() {
return fmt.Errorf("%w: invalid persistence mode %q", storage.ErrValidation, patch.Persistence.Value)
}
return nil
}
// ValidateMetadataPatch checks mutually exclusive metadata edits.
func ValidateMetadataPatch(patch publicops.MetadataPatch) error {
if patch.Replace.Set && (patch.Merge.Set || len(patch.Set) > 0 || len(patch.Unset) > 0) {
return fmt.Errorf("%w: cannot combine metadata replacement with incremental metadata edits", storage.ErrValidation)
}
return nil
}
// ValidateScalarUpdates checks typed scalar values before they reach SQL.
func ValidateScalarUpdates(ctx context.Context, tx DBTX, updates map[string]interface{}) error {View on GitHub (pinned to 71377f2769)
Solutions
- Clamp/reject estimates before patching: require 0 <= minutes <= the accepted maximum
- Parse durations into minutes explicitly (hours*60) instead of passing raw units
- Check the wrapped inner error to confirm which bound was violated
Example fix
// before
patch.EstimatedMinutes = publicops.SetField[int]{Set: true, Value: mins} // mins could be -5
// after
if mins < 0 || mins > maxEstimatedMinutes { return fmt.Errorf("estimate out of range: %d", mins) }
patch.EstimatedMinutes = publicops.SetField[int]{Set: true, Value: mins} Defensive patterns
Strategy: validation
Validate before calling
if patch.EstimatedMinutes.Set {
if err := types.ValidateIssueEstimatedMinutes(patch.EstimatedMinutes.Value); err != nil {
return fmt.Errorf("pre-check estimate: %w", err)
}
} Type guard
func estimateOK(m int) bool { return types.ValidateIssueEstimatedMinutes(m) == nil } Try / catch
if err := issueops.ExecuteUpdate(ctx, tx, req); errors.Is(err, storage.ErrValidation) {
// handle estimate out-of-range: clamp or prompt user
} Prevention
- Clamp estimates to [0, max] at the input layer
- Convert hours/days to minutes explicitly before patching
- Sanitize imported data (skip or fix negative/placeholder estimates) before bulk updates
When it happens
Trigger: ExecuteUpdate with Patch.EstimatedMinutes.Set=true and a value failing types.ValidateIssueEstimatedMinutes — typically a negative duration or a value above the allowed ceiling.
Common situations: Import scripts writing estimated durations from spreadsheets with negative or placeholder values (-1, 999999); UIs letting users type minutes freely without bounds; unit confusion (hours entered where minutes are expected, producing huge numbers).
Related errors
- %w: update title: %w
- %w: update priority: %w
- no store is open for this workspace
- not found
- db: ChildCounterSQLRepository.NextChildID: parentID must not
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/50b8a3013f8efbce.
Report an issue: GitHub.