{"record":{"id":"a2ffd6956fbd71ee","repo":"Stirling-Tools/Stirling-PDF","slug":"no-operations-in-automation","errorCode":null,"errorMessage":"No operations in automation","messagePattern":"No operations in automation","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/core/utils/automationExecutor.ts","lineNumber":231,"sourceCode":"  }\n};\n\n/**\n * Execute an entire automation sequence\n */\nexport const executeAutomationSequence = async (\n  automation: any,\n  initialFiles: File[],\n  toolRegistry: ToolRegistry,\n  onStepStart?: (stepIndex: number, operationName: string) => void,\n  onStepComplete?: (stepIndex: number, resultFiles: File[]) => void,\n  onStepError?: (stepIndex: number, error: string) => void,\n): Promise<File[]> => {\n  console.log(`🚀 Starting automation: ${automation.name || \"Unnamed\"}`);\n  console.log(`📁 Input: ${initialFiles.length} file(s)`);\n\n  if (!automation?.operations || automation.operations.length === 0) {\n    throw new Error(\"No operations in automation\");\n  }\n\n  let currentFiles = [...initialFiles];\n  const automationPrefix = automation.name\n    ? `${automation.name}_`\n    : \"automated_\";\n\n  for (let i = 0; i < automation.operations.length; i++) {\n    const operation = automation.operations[i];\n\n    console.log(\n      `\\n📋 Step ${i + 1}/${automation.operations.length}: ${operation.operation}`,\n    );\n    console.log(`   Input: ${currentFiles.length} file(s)`);\n\n    try {\n      onStepStart?.(i, operation.operation);\n","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/utils/automationExecutor.ts#L213-L249","documentation":"Thrown by executeAutomationSequence when automation.operations is missing or is an empty array. The executor iterates over operations to run each step; with zero steps there is nothing to execute, so it fails fast rather than silently returning the input files. This guards against a degenerate or incompletely-built automation being run.","triggerScenarios":"Calling executeAutomationSequence(automation, files, registry, ...) where automation.operations is undefined, null, or [] — e.g. a newly-created automation with no steps added, a deserialized automation that lost its operations field, or an automation object constructed inline without steps.","commonSituations":"User clicks 'Run' on a freshly-created automation before adding any operations. An automation was imported/loaded but the operations array was stripped or nested under a different key. The automation builder allowed saving an empty sequence.","solutions":["Ensure at least one operation is added to the automation before calling executeAutomationSequence.","Add a UI guard (disabled Run button) when automation.operations.length === 0.","Validate the automation object shape (has operations array with length > 0) before passing it to the executor.","If loading from storage, verify the operations field survived serialization."],"exampleFix":"// before\nexecuteAutomationSequence({ name: 'x', operations: [] }, files, reg);\n// after\nif (!automation.operations?.length) { warn('Add at least one step'); return; }\nexecuteAutomationSequence(automation, files, reg);","handlingStrategy":"validation","validationCode":"function hasOperations(automation: any): automation is { operations: unknown[] } {\n  return !!automation && Array.isArray(automation.operations) && automation.operations.length > 0;\n}\n\nif (!hasOperations(automation)) {\n  throw new Error('Cannot run an automation with no steps.');\n}","typeGuard":"function isRunnableAutomation(a: unknown): a is { operations: Array<{ operation: string }> } {\n  return !!a && typeof a === 'object' &&\n    Array.isArray((a as any).operations) &&\n    (a as any).operations.length > 0 &&\n    (a as any).operations.every((o: any) => typeof o?.operation === 'string');\n}","tryCatchPattern":"try {\n  await executeAutomationSequence(automation, files, registry);\n} catch (e) {\n  if (e instanceof Error && e.message === 'No operations in automation') {\n    showUser('Add at least one operation before running.');\n  } else { throw e; }\n}","preventionTips":["Disable the Run button in the UI when automation.operations is empty.","Validate the automation shape (non-empty operations array) before execution.","Prevent saving an automation with zero operations.","On load from storage, verify the operations field survived serialization."],"tags":["automation","validation","empty-state"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}