{"record":{"id":"bdf7754a90691008","repo":"odysseus-dev/odysseus","slug":"data-error-failed","errorCode":null,"errorMessage":"data.error || 'failed'","messagePattern":"data\\.error \\|\\| 'failed'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"static/js/document.js","lineNumber":3177,"sourceCode":"    if (!doc || doc.language !== 'email') return;\n    if (!fields?.forwardAttachments || !fields.sourceUid || !Array.isArray(fields.attachments) || fields.attachments.length === 0) return;\n    const sourceKey = `${fields.sourceFolder || 'INBOX'}:${fields.sourceUid}:${fields.attachments.map(a => a.index).join(',')}`;\n    if (doc._forwardedAttachmentSourceKey === sourceKey) return;\n    doc._forwardedAttachmentSourceKey = sourceKey;\n    if (!doc._composeAtts) doc._composeAtts = [];\n    const existingForwarded = new Set(doc._composeAtts.filter(a => a.forwardedSourceKey === sourceKey).map(a => String(a.sourceIndex)));\n    let added = 0;\n    for (const att of fields.attachments) {\n      const sourceIndex = String(att.index);\n      if (existingForwarded.has(sourceIndex)) continue;\n      try {\n        const folderQs = encodeURIComponent(fields.sourceFolder || 'INBOX');\n        const res = await fetch(`${API_BASE}/api/email/compose-from-attachment/${encodeURIComponent(fields.sourceUid)}/${encodeURIComponent(att.index)}?folder=${folderQs}`, {\n          method: 'POST',\n          credentials: 'same-origin',\n        });\n        const data = await res.json();\n        if (!data.success) throw new Error(data.error || 'failed');\n        doc._composeAtts.push({\n          token: data.token,\n          filename: data.filename || att.filename,\n          size: data.size || att.size || 0,\n          forwardedSourceKey: sourceKey,\n          sourceIndex,\n        });\n        added += 1;\n      } catch (err) {\n        console.error('Failed to stage forwarded attachment:', err);\n        if (uiModule) uiModule.showError(`Forward attachment failed: ${att.filename || 'attachment'}`);\n      }\n    }\n    if (added) {\n      _renderComposeAttachments();\n      clearTimeout(_autoSaveDebounce);\n      _autoSaveDebounce = setTimeout(() => { saveDocument({ silent: true }); }, 800);\n    }","sourceCodeStart":3159,"sourceCodeEnd":3195,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/document.js#L3159-L3195","documentation":"Application-level error while staging an email attachment for forwarding: POST /api/email/compose-from-attachment/{uid}/{index} answered 200 but the JSON body has success falsy, so data.error (or 'failed') is thrown per attachment. Other attachments in the loop still attempt to stage; each failure shows a toast.","triggerScenarios":"200 with {success:false,error:'message not found'} — the source uid was expunged or moved to another folder; attachment index stale after the message changed on the server; backend temp-token store full or unwritable.","commonSituations":"Message moved/deleted between opening the email and clicking forward; folder param defaulting to INBOX when the message lives elsewhere; backend cache directory permissions; concurrent forwarding of the same attachment.","solutions":["Read data.error for the backend reason; usually the message is gone from the given folder","Re-fetch/re-render the email document to refresh uid, folder, and attachment indices, then retry forward","Ensure the folder query param matches the folder the message actually resides in","Check backend temp storage health if errors mention tokens or disk"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!fields.sourceUid || !Array.isArray(fields.attachments)) { uiModule?.showError?.('Email data incomplete — reload the email'); return; }\nif (!fields.sourceFolder) fields.sourceFolder = 'INBOX'; // be explicit about the default","typeGuard":null,"tryCatchPattern":"for (const att of fields.attachments) {\n  try {\n    const res = await fetch(url, { method: 'POST', credentials: 'same-origin' });\n    const data = await res.json().catch(() => ({}));\n    if (!res.ok || !data.success) {\n      console.warn(`Staging ${att.filename} failed:`, data.error || res.status);\n      continue; // skip this attachment, keep staging the rest\n    }\n    doc._composeAtts.push({ token: data.token, /* ... */ });\n    added += 1;\n  } catch (err) {\n    console.error('Failed to stage forwarded attachment:', err);\n    if (uiModule) uiModule.showError(`Forward attachment failed: ${att.filename || 'attachment'}`);\n  }\n}","preventionTips":["JSON-parse defensively: a non-JSON error page would make res.json() throw and skip the !data.success check","Skip-and-continue per attachment (loop already isolates failures — keep it) so one stale uid doesn't block the rest","Check data.success AND res.ok together — 200 with success:false is the documented failure mode"],"tags":["fetch","application-error","email","attachment","forwarding","imap"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}