we-promise/sure · error · Setting::ValidationError
%{field} must be a whole number ≥ %{minimum}.
Error message
%{field} must be a whole number ≥ %{minimum}. What it means
The hosting controller validates four numeric LLM settings — llm_context_window (min 256), llm_max_response_tokens (min 64), llm_max_items_per_call (min 1), and ai_response_timeout (min Chat::MIN_RESPONSE_TIMEOUT) — by parsing the raw param with Integer(raw, 10) and comparing against LLM_NUMERIC_MINIMUMS. Anything that strict base-10 Integer() rejects (decimals, exponent notation, thousands separators, hex) parses to nil, and values below the minimum fail the comparison; both raise Setting::ValidationError with '.invalid_llm_budget'. A blank value is valid and clears the setting (sets it to nil), so the error only fires on present-but-wrong input. The constants mirror the min: attributes on the form inputs so server-side rejects what the browser validator would.
Source
Thrown at app/controllers/settings/hostings_controller.rb:225
if hosting_params.key?(:llm_provider)
provider = hosting_params[:llm_provider].to_s
if %w[openai anthropic].include?(provider)
Setting.llm_provider = provider
end
end
LLM_NUMERIC_MINIMUMS.each do |key, minimum|
next unless hosting_params.key?(key)
raw = hosting_params[key].to_s.strip
if raw.blank?
Setting.public_send("#{key}=", nil)
next
end
parsed = Integer(raw, 10) rescue nil
if parsed.nil? || parsed < minimum
label = t("settings.hostings.openai_settings.#{key}_label")
raise Setting::ValidationError, t(".invalid_llm_budget", field: label, minimum: minimum)
end
Setting.public_send("#{key}=", parsed)
end
if hosting_params.key?(:external_assistant_url)
Setting.external_assistant_url = hosting_params[:external_assistant_url]
end
update_encrypted_setting(:external_assistant_token)
if hosting_params.key?(:external_assistant_agent_id)
Setting.external_assistant_agent_id = hosting_params[:external_assistant_agent_id]
end
update_assistant_type
redirect_to settings_hosting_path, notice: t(".success")
rescue Setting::ValidationError => errorView on GitHub (pinned to e69894adb9)
Solutions
- Enter a plain base-10 integer with no unit, comma, or decimal point (e.g. 200000 not "200K", 30 not "30s")
- Raise the value to the field minimum: context window ≥ 256, max response tokens ≥ 64, max items per call ≥ 1, response timeout ≥ Chat::MIN_RESPONSE_TIMEOUT
- To unset a budget, leave the field empty rather than typing 0 — blank clears the setting, 0 raises
- Check app/controllers/settings/hostings_controller.rb:7 LLM_NUMERIC_MINIMUMS for the authoritative floors if the form's error message is unclear
Example fix
# before Setting.llm_context_window = "128k" # or 100, or "1,024" # => Setting::ValidationError: Context Window must be a whole number ≥ 256. # after Setting.llm_context_window = 128000 # plain Integer, ≥ minimum Setting.llm_context_window = "" # also valid: clears the setting (nil)
Defensive patterns
Strategy: validation
Validate before calling
MINS = { llm_context_window: 256, llm_max_response_tokens: 64,
llm_max_items_per_call: 1, ai_response_timeout: Chat::MIN_RESPONSE_TIMEOUT.to_i }
value = params[:llm_context_window].to_s.strip
parsed = Integer(value, 10) rescue nil
ok = value.empty? || (parsed && parsed >= MINS[:llm_context_window])
raise "out of range" unless ok Type guard
def valid_llm_integer?(raw, minimum) return true if raw.to_s.strip.empty? # blank clears parsed = Integer(raw.to_s.strip, 10) rescue nil !parsed.nil? && parsed >= minimum end
Try / catch
rescue Setting::ValidationError => e # message already includes the field label and minimum; re-render form with values render :edit, status: :unprocessable_entity end
Prevention
- Never type units, commas, or decimals into numeric setting fields
- Use the browser's min validation as a hint but never rely on it server-side
- In automation, emit plain integers from templates (no quoting, no 'k' suffix)
- Leave a field empty to clear it — zero is not 'empty' to this validator
When it happens
Trigger: Typing 10.5 or 1e3 into the context-window field; entering 100 for llm_context_window (below 256); 32 for llm_max_response_tokens (below 64); 0 for llm_max_items_per_call; "1,024" with a comma; "128 " is fine after strip but "12 8" is not; submitting "5s" or "30sec" for ai_response_timeout instead of a bare number; a curl/API client bypassing the form's min validation entirely.
Common situations: Copy-pasting model spec-sheet numbers like "200K" or "128k tokens" with a unit suffix; assuming the timeout field takes seconds-with-unit like Docker or nginx configs; automated config management (Ansible/terraform templates) rendering quoted decimals; browser validation skipped because the form was submitted programmatically.
Related errors
- {e.record.errors.full_messages.to_sentence.presence || e.mes
- Anthropic Model is required when a custom Base URL is set.
- {result.error}
- validation_failed
- validation_failed
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/171cf9de2d0fdc84.
Report an issue: GitHub.