we-promise/sure · error · Setting::ValidationError

Anthropic Base URL must be an http(s) URL.

Error message

Anthropic Base URL must be an http(s) URL.

What it means

When the self-hosting settings form submits an anthropic_base_url, the controller strips it and runs URI.parse, then requires the result to be a URI::HTTP (or URI::HTTPS). Any other outcome — a different scheme or a scheme-less string that parses into URI::Generic — raises Setting::ValidationError with the translated '.invalid_anthropic_base_url' message. This guards Provider::Anthropic from being handed an endpoint it cannot call.

Source

Thrown at app/controllers/settings/hostings_controller.rb:186

    if hosting_params.key?(:openai_model)
      Setting.openai_model = hosting_params[:openai_model]
    end

    if hosting_params.key?(:openai_json_mode)
      Setting.openai_json_mode = hosting_params[:openai_json_mode].presence
    end

    update_encrypted_setting(:anthropic_access_token)

    if hosting_params.key?(:anthropic_base_url)
      raw_base_url = hosting_params[:anthropic_base_url].to_s.strip
      if raw_base_url.blank?
        Setting.anthropic_base_url = nil
      else
        parsed = URI.parse(raw_base_url) rescue nil
        unless parsed.is_a?(URI::HTTP)
          raise Setting::ValidationError, t(".invalid_anthropic_base_url")
        end
        # A custom Anthropic-compatible endpoint requires a model — Provider::Anthropic
        # raises without one. Validate the pair together (mirrors the OpenAI branch), using
        # the submitted model when present so a blanked model field is caught too.
        effective_model =
          if hosting_params.key?(:anthropic_model)
            hosting_params[:anthropic_model].to_s.strip
          else
            Setting.anthropic_model.to_s.strip
          end
        if effective_model.blank?
          raise Setting::ValidationError, t(".anthropic_model_required_for_base_url")
        end
        Setting.anthropic_base_url = raw_base_url
      end
    end

    if hosting_params.key?(:anthropic_model)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Prefix the URL with a scheme: http://host[:port][/path] or https://host[/path]
  2. Make sure there is no leading/trailing whitespace or invisible characters if you pasted the value (the code strips outer whitespace, but embedded characters still break the parse)
  3. If you truly need a non-HTTP transport, this settings field cannot express it — put the conversion behind an http front-end
  4. If the form still fails, reproduce in console: require "uri"; URI.parse(value).is_a?(URI::HTTP) to see exactly what parses

Example fix

# before
Setting.anthropic_base_url = "my-anthropic-proxy.internal:8080"
# => Setting::ValidationError: Anthropic Base URL must be an http(s) URL.

# after
Setting.anthropic_base_url = "http://my-anthropic-proxy.internal:8080"
# => passes (URI::HTTP), and pair it with a non-blank anthropic_model
Defensive patterns

Strategy: validation

Validate before calling

# Before saving hosting params
require "uri"

def valid_http_url?(value)
  parsed = URI.parse(value.to_s.strip) rescue nil
  parsed.is_a?(URI::HTTP) && parsed.host.present?
end

url = params[:anthropic_base_url].to_s.strip
raise "bad URL" unless url.empty? || valid_http_url?(url)

Type guard

def http_url?(value)
  URI.parse(value.to_s.strip).is_a?(URI::HTTP)
rescue URI::Error, ArgumentError
  false
end

Try / catch

begin
  # save hosting settings
rescue Setting::ValidationError => e
  # e.message is the translated field error: surface next to the Base URL input
  flash.now[:alert] = e.message
end

Prevention

When it happens

Trigger: Saving the Self-Hosting settings with anthropic_base_url set to a bare host ("my-proxy.internal:8787"), a missing scheme ("api.litebbq.pro/v1"), a wrong scheme ("ftp://…", "anthropic://…"), or trailing whitespace plus scheme-less input. Note the check is on URI class, not reachability: an unreachable but well-formed http:// URL passes this guard and fails later at request time.

Common situations: Pointing the app at an Anthropic-compatible proxy (LiteLLM, claude-code-proxy, a local docker container) and typing the host without http://; copying a URL that got truncated before the scheme; treating a Docker service name ("http://ollama:11434" works, "ollama:11434" does not) as a URL; curl-style muscle memory of omitting the scheme.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/c745148df83e56f3. Report an issue: GitHub.