Stirling-Tools/Stirling-PDF · error · Error

Your Stirling-PDF server is offline and "{{endpoint}}" is no

Error message

Your Stirling-PDF server is offline and "{{endpoint}}" is not available on the local backend.

What it means

Thrown in self-hosted mode when the remote Stirling server is offline and the requested tool endpoint is not supported by the local bundled backend either. Unlike the SaaS-mode equivalent, the message is i18n-translated via i18n.t with key 'selfHosted.offline.toolNotAvailableLocally' and an {endpoint} interpolation. It is the terminal branch of the self-hosted offline fallback: the router prefers the local backend, but if the local backend cannot serve the endpoint it gives up.

Source

Thrown at frontend/editor/src/desktop/services/operationRouter.ts:281

    if (mode === "selfhosted" && operation && this.isToolEndpoint(operation)) {
      const { status } = selfHostedServerMonitor.getSnapshot();
      if (status === "offline") {
        const endpointName = this.extractEndpointName(operation);
        const localUrl = tauriBackendService.getBackendUrl();
        if (localUrl) {
          const supportedLocally =
            await endpointAvailabilityService.isEndpointSupportedLocally(
              endpointName,
              localUrl,
            );
          if (supportedLocally) {
            console.debug(
              `[operationRouter] Self-hosted server offline, routing ${operation} to local backend`,
            );
            return localUrl.replace(/\/$/, "");
          }
        }
        throw new Error(
          i18n.t(
            "selfHosted.offline.toolNotAvailableLocally",
            'Your Stirling-PDF server is offline and "{{endpoint}}" is not available on the local backend.',
            { endpoint: endpointName },
          ),
        );
      }
    }

    // Existing logic for local/remote routing
    const target = await this.getExecutionTarget(operation);

    if (target === "local") {
      // Use dynamically assigned port from backend service
      const backendUrl = tauriBackendService.getBackendUrl();
      if (!backendUrl) {
        throw new Error(
          "Backend URL not available - backend may still be starting",

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Restore the remote Stirling server (it is the authoritative source for this operation); the router will route to it once status returns to 'online'.
  2. Run a fuller local backend variant (standard instead of ultra-lite) that includes the endpoint.
  3. Confirm the local backend is actually healthy before concluding it cannot serve the endpoint — a still-booting local backend can answer capability probes incorrectly.
  4. Gate the tool in the UI on server online state so the user cannot invoke an unsupported tool while offline.

Example fix

// before — throws when localUrl is null OR local probe fails
if (localUrl) {
  const supportedLocally = await endpointAvailabilityService.isEndpointSupportedLocally(endpointName, localUrl);
  if (supportedLocally) return localUrl.replace(/\/$/, "");
}
throw new Error(i18n.t(...));

// after — distinguish 'no local backend yet' (transient) from 'genuinely unsupported'
if (!localUrl) throw new BackendNotReadyError();
if (!supportedLocally) throw new OperationUnavailableOfflineError(endpointName);
Defensive patterns

Strategy: validation

Validate before calling

import { selfHostedServerMonitor } from "@app/desktop/services/selfHostedServerMonitor";
import { tauriBackendService } from "@app/desktop/services/tauriBackendService";
import { endpointAvailabilityService } from "@app/desktop/services/endpointAvailabilityService";

async function canRunOffline(endpointName: string): Promise<boolean> {
  if (selfHostedServerMonitor.getSnapshot().status !== "offline") return true;
  const localUrl = tauriBackendService.getBackendUrl();
  if (!localUrl) return false;
  return endpointAvailabilityService.isEndpointSupportedLocally(endpointName, localUrl);
}

Type guard

function isOfflineToolUnavailable(e: unknown): boolean {
  return e instanceof Error && /server is offline/.test(e.message);
}

Try / catch

try {
  const base = await router.getBaseUrlForOperation(operation);
} catch (e) {
  if (isOfflineToolUnavailable(e)) { showOfflineUnavailableNotice(); return; }
  throw e;
}

Prevention

When it happens

Trigger: mode === 'selfhosted' + operation is a tool endpoint + selfHostedServerMonitor snapshot status === 'offline' + localUrl is present (getBackendUrl non-null) + isEndpointSupportedLocally(endpointName, localUrl) === false. Also thrown when localUrl itself is null (no local backend port yet) while the server is offline.

Common situations: The user's remote Stirling server is down (network outage, server crash) and they tried a tool the local ultra-lite backend does not include; the local backend is a lite variant missing the operation; the server went offline mid-session and the capability probe against the local backend returns a false negative because the local backend is still booting.

Related errors


AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13). Data as JSON: /api/errors/9dbdd8e77dba9de1. Report an issue: GitHub.