different-ai/openwork · error

No OpenWork link was returned.

Error message

No OpenWork link was returned.

What it means

workspace-claim-screen.tsx throws this when the desktop handoff request succeeds (2xx) but `getOpenworkUrl(payload)` cannot find an `openwork` URL in the response. The server acknowledged the handoff but did not return the link the client needs to continue.

Source

Thrown at ee/apps/den-web/app/(den)/_components/workspace-claim-screen.tsx:216

  }, [claimBusy, claimedOrg, sessionHydrated, token, user]);

  async function createDesktopHandoff(): Promise<string> {
    const { response, payload } = await requestJson(
      "/v1/auth/desktop-handoff",
      {
        method: "POST",
        body: JSON.stringify({ desktopScheme: "openwork" }),
      },
      12000,
    );

    if (!response.ok) {
      throw new Error(getErrorMessage(payload, `Could not prepare an OpenWork link (${response.status}).`));
    }

    const openworkUrl = getOpenworkUrl(payload);
    if (!openworkUrl) {
      throw new Error("No OpenWork link was returned.");
    }

    return openworkUrl;
  }

  async function handleOpenDesktop() {
    setHandoffBusy(true);
    setHandoffError(null);
    setHandoffAttempted(true);

    try {
      window.location.assign(await createDesktopHandoff());
    } catch (error) {
      setHandoffError(error instanceof Error ? error.message : "Could not open OpenWork.");
    } finally {
      setHandoffBusy(false);
    }
  }

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Log the raw handoff response payload and compare against getOpenworkUrl's expected field
  2. Align client and server on the same response schema (add a contract test)
  3. Fix the server so it either returns the openwork URL or a proper error status
  4. Hard-refresh / redeploy the web client so it is not running a stale bundle

Example fix

// before
const openworkUrl = getOpenworkUrl(payload);
if (!openworkUrl) {
  throw new Error("No OpenWork link was returned.");
}
// after
const openworkUrl = getOpenworkUrl(payload) ?? (typeof payload?.link === "string" ? payload.link : null);
if (!openworkUrl) {
  throw new Error("No OpenWork link was returned.");
}
Defensive patterns

Strategy: type-guard

Validate before calling

const payload = await res.json();
const url = (payload as { openworkUrl?: unknown }).openworkUrl;
if (typeof url !== "string" || !url.startsWith("openwork://")) {
  console.warn("handoff response missing openwork URL", payload);
}

Type guard

function hasOpenworkUrl(p: unknown): p is { openworkUrl: string } {
  return typeof p === "object" && p !== null && "openworkUrl" in p && typeof (p as { openworkUrl: unknown }).openworkUrl === "string" && (p as { openworkUrl: string }).openworkUrl.length > 0;
}

Try / catch

try {
  const url = getOpenworkUrl(payload);
  if (!url) throw new Error("No OpenWork link was returned.");
} catch (err) {
  showRetryableError("We could not generate your OpenWork link. Please try again or use sign-in code handoff instead.");
}

Prevention

When it happens

Trigger: Handoff endpoint returns 2xx with a payload lacking the openwork URL field: `{}` , `{ url: null }`, or a differently-named field after an API change.

Common situations: Server/client API contract drift after a field rename; server bug where link generation silently fails but still returns 200; middleware stripping fields from the response; stale client bundle calling an updated server.

Related errors


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