{"record":{"id":"f352fec6bc4fcbaf","repo":"block/buzz","slug":"error-message","errorCode":null,"errorMessage":"${error.message}","messagePattern":"\\$\\{error\\.message\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"desktop/src/shared/api/tauriMedia.ts","lineNumber":51,"sourceCode":"  try {\n    return await invokeTauriRaw<BlobDescriptor>(\n      \"upload_media_bytes_raw\",\n      bytes,\n      {\n        headers,\n      },\n    );\n  } catch (error) {\n    if (error instanceof Error) throw error;\n    if (typeof error === \"string\" && error.trim()) throw new Error(error);\n    if (\n      typeof error === \"object\" &&\n      error !== null &&\n      \"message\" in error &&\n      typeof error.message === \"string\" &&\n      error.message.trim()\n    ) {\n      throw new Error(error.message);\n    }\n    throw new Error(\"Media upload failed.\");\n  }\n}\n\n/** Stop the native HTTP request associated with a background media upload. */\nexport async function cancelMediaUpload(progressId: string): Promise<void> {\n  await invokeTauri(\"cancel_media_upload\", { progressId });\n}\n\n/** Release the renderer's cancellation ownership after an upload settles. */\nexport async function releaseMediaUpload(progressId: string): Promise<void> {\n  await invokeTauri(\"release_media_upload\", { progressId });\n}\n\n/**\n * Open a native single-file picker constrained to images and upload the\n * chosen file. Non-image files are rejected in Rust (via MIME sniffing)","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/desktop/src/shared/api/tauriMedia.ts#L33-L69","documentation":"When the Tauri IPC rejection is an object carrying a non-empty string `message` field (common for serialized Rust/serde errors), uploadMediaFile rethrows new Error(error.message). The literal message is the backend-provided text.","triggerScenarios":"upload_media_bytes_raw rejects with { message: \"...\" } — e.g. structured Tauri plugin errors, HTTP status errors from the native uploader, or serialized io::Error.","commonSituations":"Blossom/S3 media endpoint returning 401/413/500; native HTTP client connection errors; CDN rejecting the blob.","solutions":["Inspect error.message for the underlying cause and address it (auth token, size limit, connectivity).","Ensure the media server URL/config in the app settings is correct.","Re-authenticate if the message indicates auth failure.","Add size/type pre-validation to avoid server-side rejection."],"exampleFix":"// before\ncatch (e) { alert('upload problem'); }\n// after\ncatch (e) {\n  const msg = e instanceof Error ? e.message : (e?.message ?? String(e));\n  if (msg.includes('401')) await reauthAndRetry(); else throw new Error(msg);\n}","handlingStrategy":"try-catch","validationCode":"if (file.size > MAX_UPLOAD_BYTES) throw new Error('File too large before upload');\n","typeGuard":"function hasMessage(e: unknown): e is { message: string } { return typeof e === 'object' && e !== null && 'message' in e && typeof (e as {message:unknown}).message === 'string'; }\n","tryCatchPattern":"try { await uploadMediaFile(file); } catch (e) { const msg = hasMessage(e) ? e.message : (e instanceof Error ? e.message : String(e)); handleError(msg); }\n","preventionTips":["Handle both string and {message} rejection shapes from Tauri IPC","Re-authenticate when message indicates 401","Pre-check server quotas and file size limits"],"tags":["upload","tauri","error-normalization"],"backgroundTag":"media-upload-failed","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-09-05T18:13:50.666Z","contentChangedAt":"2026-09-05T18:13:50.666Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}