invoke-ai/InvokeAI · error · ValueError

source_url must be a string

Error message

source_url must be a string

What it means

validate_source_url is a Pydantic before-validator on the model-install/source_url field of model record schemas. If a value is provided for source_url it must be a string; passing any other type raises this ValueError during model record construction/validation.

Source

Thrown at invokeai/app/services/model_records/model_records_base.py:102

    tags: Set[str] = Field(description="tags associated with model")


class ModelRecordChanges(BaseModelExcludeNull):
    """A set of changes to apply to a model."""

    # Changes applicable to all models
    source: Optional[str] = Field(description="original source of the model", default=None)
    source_type: Optional[ModelSourceType] = Field(description="type of model source", default=None)
    source_api_response: Optional[str] = Field(description="metadata from remote source", default=None)
    source_url: Optional[str] = Field(description="Optional URL for the model (e.g. download page)", default=None)

    @field_validator("source_url", mode="before")
    @classmethod
    def validate_source_url(cls, v: Any) -> Optional[str]:
        if v is None or v == "":
            return None
        if not isinstance(v, str):
            raise ValueError("source_url must be a string")
        if not v.startswith(("https://", "http://")):
            raise ValueError("source_url must be an http or https URL")
        return v

    name: Optional[str] = Field(description="Name of the model.", default=None)
    path: Optional[str] = Field(description="Path to the model.", default=None)
    description: Optional[str] = Field(description="Model description", default=None)
    base: Optional[BaseModelType] = Field(description="The base model.", default=None)
    type: Optional[ModelType] = Field(description="Type of model", default=None)
    key: Optional[str] = Field(description="Database ID for this model", default=None)
    hash: Optional[str] = Field(description="hash of model file", default=None)
    file_size: Optional[int] = Field(description="Size of model file", default=None)
    format: Optional[str] = Field(description="format of model file", default=None)
    trigger_phrases: Optional[set[str]] = Field(description="Set of trigger phrases for this model", default=None)
    default_settings: Optional[
        MainModelDefaultSettings
        | LoraModelDefaultSettings
        | ControlAdapterDefaultSettings

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Coerce the value to a plain string before submission (str(url)).
  2. Ensure API clients serialize URLs as strings in JSON payloads.
  3. Fix form/deserialization code so URL fields round-trip as strings.
  4. If empty is intended, send null or "" rather than a non-string.

Example fix

// before
const payload = { source_url: new URL('https://huggingface.co/org/repo') };
// after
const payload = { source_url: 'https://huggingface.co/org/repo' };
Defensive patterns

Strategy: type-guard

Validate before calling

const sourceUrl: unknown = payload.source_url;
if (sourceUrl != null && sourceUrl !== '' && typeof sourceUrl !== 'string') {
  throw new TypeError('source_url must be a string');
}

Type guard

function isStringUrl(v: unknown): v is string {
  return v === null || v === '' || typeof v === 'string';
}

Try / catch

try {
  await api.addModelRecord(payload);
} catch (e) {
  if (String(e).includes('source_url must be a string')) {
    payload.source_url = String(payload.source_url ?? '');
    return api.addModelRecord(payload);
  }
  throw e;
}

Prevention

When it happens

Trigger: Constructing a model install/record payload where source_url is a URL object, Path, int, dict, or parsed URL instance instead of a plain string, e.g. source_url: new URL(...) or an AnyHttpUrl object from a previous parse.

Common situations: JavaScript/TypeScript clients passing URL objects; Python callers passing pydantic AnyHttpUrl/Url instances or pathlib.Path instead of str; deserialized YAML/JSON where a resolver produced a non-string mapping.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/20143658a218cb0c. Report an issue: GitHub.