Skip to content

fix(editor): show an error instead of an endless skeleton when metadata fails#2044

Open
DPS0340 wants to merge 1 commit into
CapSoftware:mainfrom
DPS0340:fix/editor-meta-load-error
Open

fix(editor): show an error instead of an endless skeleton when metadata fails#2044
DPS0340 wants to merge 1 commit into
CapSoftware:mainfrom
DPS0340:fix/editor-meta-load-error

Conversation

@DPS0340

@DPS0340 DPS0340 commented Jul 26, 2026

Copy link
Copy Markdown

Closes #1812.

What @shreyastaware saw

Open a previous recording → the editor shows the loading skeleton → it never leaves. Their screenshot is the skeleton, and they noted "couldn't find logs in macOS", so there was nothing to report beyond "it hangs".

Why it hangs forever

getRecordingMetaByPath is Result<RecordingMeta, String> — it fails whenever recording-meta.json is missing or unparseable (RecordingMeta::load_for_project does read_to_string then serde_json::from_str, both fallible). But the status derivation only consulted data:

const meta = rawMetaQuery.data;
if (!meta) return "loading" as const;

A failed query has no data either, so failure was indistinguishable from a query still in flight. The <Switch> had arms for importing and ready and nothing for failure, so it fell through to the spinner fallback — permanently. No error, no log, no way forward.

EditorErrorScreen already exists for exactly this, complete with a recovery path and a "reveal in folder" button. It was simply unreachable on a metadata failure. This wires it up.

The second half

The error also has to outrank the lockedToImporting latch. That latch exists so the UI doesn't flicker while metadata is re-read, but if the metadata stops being readable mid-import there is nothing left to wait for — without this it would strand the import screen for the same reason it used to strand the skeleton.

Verification

Both derivations are extracted into import-status.ts so the state machine is testable without a running editor, and covered by 8 vitest cases.

Confirmed the tests actually catch the bug rather than just passing: reverting only the two error branches in the extracted module gives

Tests  3 failed | 5 passed (8)

and restoring them gives 8 passed. npx tsc --noEmit -p apps/desktop/tsconfig.json0 errors; biome check clean.

What I have not done: reproduce the hang on a real build. I can't compile the desktop crate here — the build script needs binaries/cap-muxer-* and target/native-deps/Spacedrive.framework, and it fails identically on unmodified main (verified by stashing). So the diagnosis is traced statically from the Rust command through to the <Switch>, and the fix is covered by unit tests rather than an end-to-end run.

One thing worth a maintainer's judgement: I did not try to work out why @shreyastaware's recording-meta.json was unreadable. This makes the failure visible and offers recovery, but if there is an underlying cause producing broken recordings on macOS 26.4.1, that is a separate issue and this shouldn't be taken as closing it.

…ta fails

getRecordingMetaByPath returns Result<RecordingMeta, String> and fails
whenever recording-meta.json is missing or unparseable. The editor's
status derivation only looked at the query's data:

    const meta = rawMetaQuery.data;
    if (!meta) return "loading" as const;

A failed query has no data either, so a failure was indistinguishable
from a query still in flight. The Switch had no arm for the error case,
so it fell through to the spinner and stayed there -- which is CapSoftware#1812:
the editor opens on the loading skeleton and never leaves it.

EditorErrorScreen already existed for this, complete with a recovery
path; it was simply unreachable on a metadata failure. It is now wired
to the new error state.

The error also has to outrank the lockedToImporting latch. That latch
exists so the UI doesn't flicker while metadata is re-read, but if the
metadata stops being readable mid-import there is nothing to wait for,
and it would strand the import screen the same way.

Extracted both derivations into import-status.ts so the state machine is
testable without a running editor, and added 8 vitest cases. Confirmed
they fail without the fix: reverting just the two error branches gives
3 failed | 5 passed.
Comment on lines +17 to +20
if (query.isError) return "error";
if (!query.data) return "loading";

const meta = query.data as { status?: unknown };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small hardening: "status" in meta will throw if query.data ever isn't an object (unexpected payload / bad deserialization). Guarding the shape keeps this from turning into a runtime crash.

Suggested change
if (query.isError) return "error";
if (!query.data) return "loading";
const meta = query.data as { status?: unknown };
if (query.isError) return "error";
if (!query.data) return "loading";
if (typeof query.data !== "object" || query.data === null) return "ready";
const meta = query.data as { status?: unknown };

Comment on lines +205 to +210
<Match when={importStatus() === "error"}>
<EditorErrorScreen
error={getEditorErrorMessage(rawMetaQuery.error)}
projectPath={projectPath() ?? ""}
/>
</Match>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EditorErrorScreen uses projectPath for recover/open-folder; passing "" feels a bit risky. Might be nicer to gate this branch on projectPath() like the importing/ready branches.

Suggested change
<Match when={importStatus() === "error"}>
<EditorErrorScreen
error={getEditorErrorMessage(rawMetaQuery.error)}
projectPath={projectPath() ?? ""}
/>
</Match>
<Match
when={importStatus() === "error" ? (projectPath() ?? null) : null}
>
{(path) => (
<EditorErrorScreen
error={getEditorErrorMessage(rawMetaQuery.error ?? "Unknown error")}
projectPath={path()}
/>
)}
</Match>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Edit Previous Recording goes to Loading Skeleton Screen to Unexpectedly App Crash

1 participant