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
- Log the raw handoff response payload and compare against getOpenworkUrl's expected field
- Align client and server on the same response schema (add a contract test)
- Fix the server so it either returns the openwork URL or a proper error status
- 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
- Add a contract test for the handoff response schema shared by client and server
- Have the server return 500 (not 200 with an empty payload) when link generation fails
- Deploy client and server together when the handoff schema changes
- Log the raw payload when getOpenworkUrl returns null
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
- This install link returned incomplete setup details.
- Organization was created, but no slug was returned.
- Could not prepare an OpenWork link (${response.status}).
- Failed to load organizations.
- Failed to create organization.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/802eb2cc38bfcb29.
Report an issue: GitHub.