{
  "id": "bd6d25eb-a15d-4a45-984c-4219ae7f06c2",
  "name": "StudyFlow Workflow Errors",
  "nodes": [
    {
      "parameters": {},
      "id": "error-trigger",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "typeVersion": 1,
      "position": [
        -700,
        220
      ]
    },
    {
      "parameters": {
        "jsCode": "const execution = $json.execution || {};\nconst workflow = execution.workflow || {};\nconst lastNode = execution.lastNodeExecuted || 'unknown';\nconst error = execution.error || {};\nconst runData = execution.data?.resultData?.runData || {};\nconst candidateNodes = [\n  'Validate + Init',\n  'Create Run Record',\n  'Extract Text',\n  'Persist Source Document',\n  'Estimate Workspace Strategy',\n  'Render Friendly HTML',\n  'Build Retrieval Index',\n  'Generate Study Pack (Single Pass)',\n  'Generate Study Pack (Chunked)',\n  'Normalize Workspace Result',\n  'Persist Workspace Result',\n  'Mark Run Success',\n  'Validate Chat Request',\n  'Load Workspace Context',\n  'Retrieve Relevant Chunks',\n  'Fetch Conversation History',\n  'Prepare Grounded Answer Input',\n  'Generate Grounded Answer',\n  'Persist Chat Turn'\n];\nlet runId = null;\nlet documentId = null;\nlet userId = null;\nlet conversationId = null;\nfor (const nodeName of candidateNodes) {\n  const nodeRuns = runData[nodeName];\n  if (!Array.isArray(nodeRuns) || !nodeRuns.length) continue;\n  const latest = nodeRuns[nodeRuns.length - 1]?.data?.main?.[0]?.[0]?.json;\n  if (!latest || typeof latest !== 'object') continue;\n  runId = runId || latest.runId || latest.body?.runId || null;\n  documentId = documentId || latest.documentId || latest.body?.documentId || null;\n  userId = userId || latest.userId || latest.body?.userId || null;\n  conversationId = conversationId || latest.conversationId || latest.body?.conversationId || null;\n}\nreturn [{\n  json: {\n    workflowName: workflow.name || 'unknown',\n    workflowId: workflow.id || null,\n    lastNode,\n    runId,\n    documentId,\n    userId,\n    conversationId,\n    errorMessage: error.message || 'Workflow execution failed.',\n    stack: error.stack || null,\n    failedAt: new Date().toISOString()\n  }\n}];"
      },
      "id": "extract-failure-context",
      "name": "Extract Failure Context",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -420,
        220
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/review-queue'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { workflow: $json.workflowName, workflowId: $json.workflowId, runId: $json.runId, documentId: $json.documentId, userId: $json.userId, conversationId: $json.conversationId, failedNode: $json.lastNode, errorMessage: $json.errorMessage, stack: $json.stack, failedAt: $json.failedAt } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "write-review-queue",
      "name": "Write Review Queue",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -120,
        140
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{Boolean($env.STUDYFLOW_SLACK_WEBHOOK_URL)}}",
              "operation": "isTrue"
            }
          ]
        }
      },
      "id": "if-slack-configured",
      "name": "If Slack Configured",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -120,
        340
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_SLACK_WEBHOOK_URL}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { text: `StudyFlow workflow failure\\nWorkflow: ${$json.workflowName}\\nNode: ${$json.lastNode}\\nRun ID: ${$json.runId || 'n/a'}\\nDocument ID: ${$json.documentId || 'n/a'}\\nConversation ID: ${$json.conversationId || 'n/a'}\\nError: ${$json.errorMessage}` } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "notify-slack",
      "name": "Notify Slack",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        180,
        340
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{Boolean($json.runId)}}",
              "operation": "isTrue"
            }
          ]
        }
      },
      "id": "if-run-known",
      "name": "If Run Known",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        180,
        140
      ]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/runs/' + $json.runId}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { status: 'failed', failedAt: $json.failedAt, failedNode: $json.lastNode, errorMessage: $json.errorMessage } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "mark-run-failed",
      "name": "Mark Run Failed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        500,
        140
      ]
    }
  ],
  "connections": {
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Extract Failure Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Failure Context": {
      "main": [
        [
          {
            "node": "Write Review Queue",
            "type": "main",
            "index": 0
          },
          {
            "node": "If Slack Configured",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write Review Queue": {
      "main": [
        [
          {
            "node": "If Run Known",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Slack Configured": {
      "main": [
        [
          {
            "node": "Notify Slack",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "If Run Known": {
      "main": [
        [
          {
            "node": "Mark Run Failed",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveManualExecutions": true,
    "timezone": "UTC"
  },
  "pinData": {},
  "versionId": "268d517b-251e-45c3-8439-19bf1ac5dc91",
  "meta": {
    "templateCredsSetupCompleted": false,
    "notes": "Companion workflow for StudyFlow document ingestion and grounded chat. Captures failures and writes them to the review queue."
  },
  "active": false
}
