we-promise/sure · error · Provider::Openai::Error

Model is required when using a custom OpenAI‑compatible prov

Error message

Model is required when using a custom OpenAI‑compatible provider

What it means

Raised in Provider::Openai#initialize when a custom provider (a uri_base was supplied, e.g. an OpenAI-compatible gateway like Ollama, LM Studio, or OpenRouter) is configured without a model. With first-party OpenAI the model can default via OPENAI_MODEL / Setting.openai_model / DEFAULT_MODEL (gpt-4.1), but an arbitrary endpoint has no safe default — the gem would otherwise send requests with a model the gateway does not host. Note the message contains a Unicode non-breaking hyphen in 'OpenAI‑compatible', so match on a regex or a normalized string, not a copy-pasted literal.

Source

Thrown at app/models/provider/openai.rb:31

  def self.effective_model
    ENV.fetch("OPENAI_MODEL") { Setting.openai_model }.presence || DEFAULT_MODEL
  end

  def self.configured?
    ENV["OPENAI_ACCESS_TOKEN"].present? || Setting.openai_access_token.present?
  end

  def initialize(access_token, uri_base: nil, model: nil)
    client_options = { access_token: access_token }
    llm_uri_base = uri_base.presence
    llm_model = model.presence
    client_options[:uri_base] = llm_uri_base if llm_uri_base.present?
    client_options[:request_timeout] = ENV.fetch("OPENAI_REQUEST_TIMEOUT", 60).to_i

    @client = ::OpenAI::Client.new(**client_options)
    @uri_base = llm_uri_base
    if custom_provider? && llm_model.blank?
      raise Error, "Model is required when using a custom OpenAI‑compatible provider"
    end
    @default_model = llm_model.presence || self.class.effective_model
  end

  def supports_model?(model)
    # If using custom uri_base, support any model
    return true if custom_provider?

    # Otherwise, check if model starts with any supported OpenAI prefix
    SUPPORTED_MODELS.any? { |prefix| model.start_with?(prefix) }
  end

  def supports_responses_endpoint?
    return @supports_responses_endpoint if defined?(@supports_responses_endpoint)

    env_override = ENV["OPENAI_SUPPORTS_RESPONSES_ENDPOINT"]
    if env_override.to_s.present?
      return @supports_responses_endpoint = ActiveModel::Type::Boolean.new.cast(env_override)

View on GitHub (pinned to e69894adb9)

Solutions

  1. Pass an explicit model the endpoint serves: Provider::Openai.new(key, uri_base: ..., model: "llama3.1").
  2. Set OPENAI_MODEL in env (or Setting.openai_model) when wiring a custom provider app-wide.
  3. Add a form-level validation requiring model when uri_base is present so the error never reaches the initializer.
  4. When rescuing this error by message, match a prefix like /^Model is required/ instead of the full string to dodge the non-breaking hyphen.

Example fix

# before
Provider::Openai.new(api_key, uri_base: setting.uri_base)

# after
raise ArgumentError, "model required for custom provider" if setting.uri_base.present? && setting.model.blank?
Provider::Openai.new(api_key, uri_base: setting.uri_base, model: setting.model)
Defensive patterns

Strategy: validation

Validate before calling

if uri_base.present? && model.blank?
  raise ArgumentError, "model is required when uri_base is set"
end

Try / catch

begin
  client = Provider::Openai.new(key, uri_base: base, model: model)
rescue Provider::Openai::Error
  flash[:alert] = "Choose a model for the custom provider" and render :edit
end

Prevention

When it happens

Trigger: Provider::Openai.new(key, uri_base: "http://localhost:11434/v1") with no model: argument; Settings UI saving a custom LLM provider base URL while the model field is left blank; OPENAI_MODEL and Setting.openai_model both unset (the default does not apply because custom_provider? short-circuits the fallback).

Common situations: Self-hosting an OpenAI-compatible endpoint and forgetting that the model list is provider-specific; copy-pasting an initializer snippet for OpenAI and only changing uri_base; blank-string model coming from a form (model.presence turns '' into nil); matching this message in a rescue and missing because of the invisible U+2011 hyphen.

Related errors


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