different-ai/openwork · error

Cannot save non-file artifact.

Error message

Cannot save non-file artifact.

What it means

The artifact save mutation in ArtifactPanelView only supports artifacts whose target is a file (path-backed). If the selected artifact's target.kind is not 'file' (e.g. a terminal/buffer or other in-memory target), there is no workspace file path to write to, so the mutation throws before calling client.writeWorkspaceFile.

Source

Thrown at apps/app/src/react-app/domains/session/artifacts/artifact-panel.tsx:184

  useEffect(() => {
    setEditing(false);
    setDraft("");
    lastSyncedRef.current = null;
    failedDraftRef.current = null;
  }, [target.id, workspaceId]);

  useEffect(() => {
    if (data?.kind === "text" && data.data !== lastSyncedRef.current) {
      lastSyncedRef.current = data.data;
      setDraft(data.data);
    }
  }, [data]);

  const { mutate, mutateAsync, isPending: isSaving } = useMutation({
    mutationFn: async (input: SaveArtifactInput) => {
      if (target.kind !== "file") {
        throw new Error("Cannot save non-file artifact.");
      }

      if (input.kind === "text") {
        return client.writeWorkspaceFile(workspaceId, { path: target.value, content: input.data, baseUpdatedAt: input.baseUpdatedAt });
      }

      return client.writeWorkspaceBinaryFile(workspaceId, { path: target.value, data: input.data, baseUpdatedAt: input.baseUpdatedAt });
    },
    onSuccess: (result, input) => {
      queryClient.setQueryData<ArtifactQueryState>(
        ["artifact-panel", workspaceId, target.id, target.updatedAt ?? null] as const,
        input.kind === "text"
          ? { kind: "text", data: input.data, updatedAt: result.updatedAt ?? null }
          : { kind: "binary", data: input.data, contentType: data?.kind === "binary" ? data.contentType : null, updatedAt: result.updatedAt ?? null },
      );

      if (input.kind === "text") {
        lastSyncedRef.current = input.data;

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Only render/enable the Save button when target.kind === "file"
  2. Before mutating, check target.kind and route non-file targets to a different save path or show a disabled state
  3. If the artifact should be file-backed, ensure the target is resolved to a workspace file path before opening the panel
  4. Catch the error in an onError handler and surface a clear 'this artifact type cannot be saved' message

Example fix

// before
await mutateAsync({ kind: "text", data, baseUpdatedAt });
// after
if (target.kind !== "file") {
  toast.error("Only file artifacts can be saved to the workspace.");
  return;
}
await mutateAsync({ kind: "text", data, baseUpdatedAt });
Defensive patterns

Strategy: validation

Validate before calling

if (target.kind !== "file") {
  toast.error("Only file artifacts can be saved to the workspace.");
  return;
}

Type guard

const isFileTarget = (t: Target): t is Extract<Target, { kind: "file" }> => t.kind === "file";

Try / catch

try {
  await mutateAsync(input);
} catch (e) {
  if (e instanceof Error && e.message === "Cannot save non-file artifact.") {
    toast.error("This artifact type cannot be saved as a file.");
  } else throw e;
}

Prevention

When it happens

Trigger: Clicking Save in the artifact panel while the panel's `target.kind` is anything other than "file"; calling the mutation with a SaveArtifactInput whose underlying target is a non-file artifact (e.g. text captured from a tool output rather than a workspace file).

Common situations: Users open an artifact derived from a chat/tool response or a virtual buffer and hit Save expecting it to persist; a target computed from stale or wrong panel state; UI allowing Save on artifact types it cannot persist.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/2f289075c98c9868. Report an issue: GitHub.