tinyhumansai/openhuman · error
DOWNLOAD_FAILED
DOWNLOAD_FAILED
Error message
${reason} What it means
Returned by downloadArtifact when the Tauri IPC command download_artifact_to_downloads throws after the artifact had already resolved successfully (resolveArtifactForExport returned ok). The copy happens inside the Rust shell, so the failure is native: the source blob vanished, the Downloads destination is missing or unwritable, the disk is full, or the invoke ran outside a real Tauri context. The JS side wraps the raw invoke error message as DOWNLOAD_FAILED.
Source
Thrown at app/src/services/artifactDownloadService.ts:256
code: 'NOT_DESKTOP',
error: 'Downloads are only available in the desktop app',
};
}
const resolved = await resolveArtifactForExport(artifactId, fallbackTitle, extension);
if (!resolved.ok) {
return { ok: false, code: resolved.code, error: resolved.error };
}
try {
const dest = await invoke<string>('download_artifact_to_downloads', {
sourcePath: resolved.sourcePath,
filename: resolved.filename,
});
return { ok: true, path: dest };
} catch (err) {
const reason = err instanceof Error ? err.message : String(err);
return { ok: false, code: 'DOWNLOAD_FAILED', error: reason };
}
}
/**
* Export an artifact via the native Save-As dialog (#3162), pre-filled
* with the artifact's filename. On the user dismissing the dialog,
* returns `{ ok: false, code: 'CANCELLED' }` (the caller should treat
* this as a no-op, not an error). If the dialog itself is unavailable —
* e.g. headless Linux with no xdg-desktop portal — falls back to the
* Downloads copy so the artifact is still recoverable.
*/
export async function saveArtifactViaDialog(
artifactId: string,
fallbackTitle: string,
extension: string
): Promise<DownloadArtifactOutcome> {
if (!isTauri()) {
return { ok: false, code: 'NOT_DESKTOP', error: 'Saving is only available in the desktop app' };View on GitHub (pinned to 7491200858)
Solutions
- Read the outcome's error field - it is the raw Tauri command error and distinguishes 'source file missing' from permission or full-disk failures.
- If the source blob is gone, call deleteArtifact(artifactId) to drop the stale ArtifactMeta row, then regenerate the artifact.
- Confirm you are inside the Tauri desktop shell (`pnpm dev:app` or a built app) and that ~/Downloads exists and is writable.
- Fall back to revealArtifactInFileManager, or re-run generation when the copy keeps failing.
Example fix
// before
const out = await downloadArtifact(id, title, ext);
if (!out.ok) showError('Download failed'); // collapses every code into one message
// after
const out = await downloadArtifact(id, title, ext);
if (!out.ok) {
if (out.code === 'DOWNLOAD_FAILED' && /no such file|not found/i.test(out.error ?? '')) {
await deleteArtifact(id); // stale row: blob already gone
showError('Artifact file is missing. Regenerating.');
} else {
showError(`Download failed: ${out.error}`);
}
} Defensive patterns
Strategy: fallback
Try / catch
downloadArtifact never throws - it returns a DownloadArtifactOutcome. Branch on code: DOWNLOAD_FAILED with a file-missing reason -> offer delete-and-regenerate or revealArtifactInFileManager as fallback; other codes -> surface the error text verbatim.
Prevention
- Reconcile UI artifact rows against core state on mount so stale blobs surface before the user clicks download.
- Always switch on outcome.code rather than collapsing all failures into one message - the reason field is the raw native error.
- Only offer download UI inside isTauri() contexts.
When it happens
Trigger: Calling downloadArtifact(artifactId) where resolveArtifactForExport succeeds but invoke('download_artifact_to_downloads', { sourcePath, filename }) rejects: the on-disk artifact file was deleted or moved after its ArtifactMeta row was registered, ~/Downloads is absent or read-only, the volume is full, or the frontend runs in a plain browser where the command cannot resolve.
Common situations: A stale artifact row still listed in the UI while another flow or the user already removed its file; macOS privacy permissions denying Downloads writes; running the UI via `pnpm dev` (browser only) instead of `pnpm dev:app`; full disks on long-lived machines.
Related errors
- Cannot reveal "${path}" on this device — it is a path on the
- Not running in Tauri
- Not running in Tauri
- Not running in Tauri
- Not running in Tauri
AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17).
Data as JSON: /api/errors/c6f4e61d422ddd28.
Report an issue: GitHub.