SillyTavern/SillyTavern · error

Internal Server Error

Error message

Internal Server Error

What it means

HTTP 500 from POST /api/horde/text-workers in src/endpoints/horde.js:77. The handler fetches https://aihorde.net/api/v2/workers?type=text via node-fetch and returns the JSON; any rejection (DNS failure, connection refused, TCP reset, response.json() parse error) lands in the catch and sendStatus(500) is emitted. There is no retry or fallback.

Source

Thrown at src/endpoints/horde.js:77

    try {
        const cachedWorkers = cache.get('workers');

        if (cachedWorkers && !request.body.force) {
            return response.send(cachedWorkers);
        }

        const agent = await getClientAgent();
        const fetchResult = await fetch('https://aihorde.net/api/v2/workers?type=text', {
            headers: {
                'Client-Agent': agent,
            },
        });
        const data = await fetchResult.json();
        cache.set('workers', data);
        return response.send(data);
    } catch (error) {
        console.error(error);
        response.sendStatus(500);
    }
});

async function getHordeTextModelMetadata() {
    const response = await fetch(HORDE_TEXT_MODEL_METADATA_URL);
    return await response.json();
}

async function mergeModelsAndMetadata(models, metadata) {
    return models.map(model => {
        const metadataModel = metadata[model.name];
        if (!metadataModel) {
            return { ...model, is_whitelisted: false };
        }
        return { ...model, ...metadataModel, is_whitelisted: true };
    });
}

View on GitHub (pinned to 8172dcd0ee)

Solutions

  1. Verify outbound HTTPS to https://aihorde.net/api/v2/workers?type=text works from the host (curl -I).
  2. If behind a proxy, set HTTPS_PROXY/HTTP_PROXY and ensure node-fetch honors it, or whitelist aihorde.net.
  3. Wait and retry: the error is often transient and the 60s cache will serve stale data on the next non-forced call.
  4. Check server console logs (console.error(error)) for the underlying node-fetch error code (ENOTFOUND, ECONNRESET, ETIMEDOUT) to pinpoint DNS vs network vs TLS.
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight reachability hint (optional)
async function hordeReachable() {
  try {
    const r = await fetch('https://aihorde.net/api/v2/status/heartbeat', { method:'HEAD' });
    return r.ok;
  } catch { return false; }
}

Try / catch

// Caller: retry transient horde worker fetch failures
async function getTextWorkers(maxAttempts = 3) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const r = await fetch('/api/horde/text-workers', { method:'POST', headers:{'Content-Type':'application/json'}, body:'{}' });
      if (r.ok) return await r.json();
      if (r.status === 500 && attempt < maxAttempts) { await new Date(); continue; }
      throw new Error('text-workers failed: ' + r.status);
    } catch (e) {
      if (attempt === maxAttempts) throw e;
    }
  }
}

Prevention

When it happens

Trigger: The server has no internet route to aihorde.net; a corporate proxy/firewall blocks the host; aihorde.net is down or returns non-JSON (HTML error page) so fetchResult.json() throws; getClientAgent() throws because getVersion() fails; cache miss with request.body.force=true forcing a live fetch during an outage.

Common situations: Running SillyTavern behind a restrictive firewall or in an offline/air-gapped environment; DNS hiccup; the user clicked 'refresh/force' which bypasses the 60s cache; node-fetch version mismatch causing undici/Headers issues; a transient aihorde.net 5xx that still serves JSON but the connection drops mid-stream.

Understand the failure class

Related errors


AI-assisted analysis of SillyTavern/SillyTavern@8172dcd0ee (2026-08-13). Data as JSON: /api/errors/1069a4440eb19fee. Report an issue: GitHub.