moeru-ai/airi · error · Error

The Base URL is not an absolute http:// or https:// address.

Error message

The Base URL is not an absolute http:// or https:// address.

What it means

request() builds the target URL with `new URL(...)` via buildUrl; if that throws (base is relative, empty, or a non-http scheme), the library rethrows this error. The engine client requires an absolute http:// or https:// Base URL because the browser/renderer fetch cannot resolve engine-relative paths.

Source

Thrown at packages/stage-ui/src/libs/providers/providers/voicevox/engine.ts:189

  const body = await response.text()
  try {
    return JSON.parse(body) as T
  }
  catch {
    throw new Error(`Speech engine answered /${endpoint} with a body that is not JSON. Check that the Base URL points at a VOICEVOX-compatible engine.`)
  }
}

async function request(
  engineRequest: VoicevoxEngineRequest,
  options?: VoicevoxEngineRequestOptions,
): Promise<Response> {
  let url: URL
  try {
    url = buildUrl(engineRequest)
  }
  catch {
    throw new Error('The Base URL is not an absolute http:// or https:// address.')
  }

  const doFetch = options?.fetch ?? globalThis.fetch
  const response = await doFetch(url, {
    method: POST_ENDPOINTS.has(engineRequest.endpoint) ? 'POST' : 'GET',
    // No engine in the family redirects. A redirect therefore means the Base URL
    // points at something else, and following it would hide that.
    redirect: 'error',
    signal: options?.signal,
    ...(engineRequest.body === undefined
      ? {}
      : { body: JSON.stringify(engineRequest.body), headers: { 'Content-Type': 'application/json' } }),
  })

  if (!response.ok) {
    const detail = (await response.text()).trim()
    const suffix = detail ? `: ${detail.slice(0, 200)}` : ''
    throw new Error(`Speech engine answered ${response.status} ${response.statusText} for /${engineRequest.endpoint}${suffix}`)

View on GitHub (pinned to 9c213115f8)

Solutions

  1. Set the Base URL to a full absolute URL including scheme, e.g. `http://127.0.0.1:50021` or `https://voicevox.example.com`.
  2. If using a local engine on https-only pages, proxy it or use `http://localhost:50021` where allowed.
  3. Trim whitespace and re-check the scheme when saving the settings field.

Example fix

// before
baseUrl: 'localhost:50021'
// after
baseUrl: 'http://localhost:50021'
Defensive patterns

Strategy: validation

Validate before calling

function isAbsoluteHttpUrl(value: string): boolean {
  try {
    const url = new URL(value)
    return url.protocol === 'http:' || url.protocol === 'https:'
  }
  catch {
    return false
  }
}
if (!isAbsoluteHttpUrl(baseUrl))
  throw new Error('Base URL must be an absolute http:// or https:// address')

Type guard

function isHttpUrl(value: unknown): value is string {
  if (typeof value !== 'string')
    return false
  try {
    return ['http:', 'https:'].includes(new URL(value).protocol)
  }
  catch {
    return false
  }
}

Prevention

When it happens

Trigger: Base URL configured as a relative path like `localhost:50021` (no scheme) or `/voicevox`, an empty string, or a scheme like `ws://`/`file://` — anything that makes `new URL(path, base)` throw or not be http(s).

Common situations: User typed the host without `http://`; trailing config value left empty after clearing a default; settings field trimmed of scheme; pasting a URL from docs that omitted the scheme.

Related errors


AI-assisted analysis of moeru-ai/airi@9c213115f8 (2026-09-02). Data as JSON: /api/errors/65da30bb119f7f80. Report an issue: GitHub.