Stirling-Tools/Stirling-PDF · error · Error
This operation has no backend endpoint and cannot be…
Error message
This operation has no backend endpoint and cannot be executed directly.
What it means
Thrown inside executeSingleFileOperation when a tool's operationConfig.endpoint resolves to a falsy value. The endpoint can be a static string or a function of the parameters; if either yields null/undefined/empty-string, the operation cannot make an HTTP POST to the backend, so execution aborts. This means the tool is registered and has an operationConfig, but that config lacks a usable endpoint.
Solutions
- Check the tool's entry in toolsTaxonomy (the ToolRegistry) and confirm operationConfig.endpoint is set to a valid backend path (e.g. '/api/v1/general/split-pages').
- If endpoint is a function, audit every parameter branch to ensure it always returns a non-empty string.
- If the tool is frontend-only by design, exclude it from the automation operation picker so it can never be routed to the executor.
- Gate the operation with a check (e.g. in the automation builder) that hides tools whose endpoint resolves to falsy.
Example fix
// before endpoint: (params) => params.mode === 'x' ? '/api/v1/x' : undefined // after endpoint: (params) => params.mode === 'x' ? '/api/v1/x' : '/api/v1/default'
Defensive patterns
Strategy: validation
Validate before calling
function hasUsableEndpoint(config: any, params: any): boolean {
const ep = typeof config.endpoint === 'function' ? config.endpoint(params) : config.endpoint;
return typeof ep === 'string' && ep.length > 0;
}
const config = toolRegistry[op]?.operationConfig;
if (!hasUsableEndpoint(config, params)) {
throw new Error(`Tool '${op}' cannot be executed (no endpoint).`);
} Type guard
function isExecutableSingleFileConfig(config: any, params: any): boolean {
if (!config || !config.buildFormData) return false;
const ep = typeof config.endpoint === 'function' ? config.endpoint(params) : config.endpoint;
return typeof ep === 'string' && ep.length > 0;
} Try / catch
try {
await executeSingleFileOperation(config, params, files, prefix);
} catch (e) {
if (e instanceof Error && e.message.includes('no backend endpoint')) {
showUser(`This tool has no direct backend endpoint and cannot run in automation.`);
} else { throw e; }
} Prevention
- Maintain a registry test asserting every executable tool has a non-empty endpoint.
- Exclude display-only/frontend-only tools from the automation operation list.
- For dynamic endpoints, ensure all parameter branches return a valid string.
- Log the resolved endpoint before execution during development.
When it happens
Trigger: executeToolOperationWithPrefix routes to the single-file branch for a tool whose operationConfig has endpoint: undefined, endpoint: null, endpoint: '' (empty string), or a dynamic endpoint function that returns undefined for the given parameters. Only single-file tools (those processed via executeSingleFileOperation) hit line 95.
Common situations: A tool was registered with only a customProcessor or with endpoint intentionally omitted because it is a frontend-only/display operation, but the automation executor was invoked on it anyway. A dynamic endpoint() function has a code path that returns undefined for an unhandled parameter combination. A tool was added to the registry skeleton but its endpoint was never wired.
Related errors
- Tool operation not supported
- Expected but file looks like .
- Invalid automation config: expected JSON object
- Invalid automation config: missing 'operations' array
- Invalid automation config: operations
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/6be014cf129193c0.
Report an issue: GitHub.
Appendix: source
Thrown at frontend/editor/src/core/utils/automationExecutor.ts:95
};
/**
* Execute single-file tool operation (processes files one at a time)
*/
const executeSingleFileOperation = async (
config: SingleFileToolOperationConfig<ErasedToolParams>,
parameters: ErasedToolParams,
files: File[],
filePrefix: string,
): Promise<File[]> => {
const resultFiles: File[] = [];
const endpoint =
typeof config.endpoint === "function"
? config.endpoint(parameters)
: config.endpoint;
if (!endpoint) {
throw new Error(
"This operation has no backend endpoint and cannot be executed directly.",
);
}
for (const file of files) {
const formData = config.buildFormData(parameters, file);
const processedFiles = await executeApiRequest(
endpoint,
formData,
[file],
filePrefix,
config.preserveBackendFilename,
);
resultFiles.push(...processedFiles);
}
return resultFiles;View on GitHub (pinned to 9ef20dcab8)