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
- Restore the remote Stirling server (it is the authoritative source for this operation); the router will route to it once status returns to 'online'.
- Run a fuller local backend variant (standard instead of ultra-lite) that includes the endpoint.
- Confirm the local backend is actually healthy before concluding it cannot serve the endpoint — a still-booting local backend can answer capability probes incorrectly.
- 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
- Disable tools the local backend cannot serve while the server is offline.
- Subscribe to selfHostedServerMonitor to update tool availability reactively.
- Distinguish 'no local backend yet' (transient) from 'unsupported' before throwing.
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
- This operation (${endpointToCheck}) is not available. It may
- Server configuration not found
- Unsupported unit: ${unit}
- Invalid real-world distance (must be positive)
- Sign up failed
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/9dbdd8e77dba9de1.
Report an issue: GitHub.