JuliusBrussee/caveman · error · Error

device login failed: server did not provide a delivery ackno

Error message

device login failed: server did not provide a delivery acknowledgement token

What it means

After a device login, acknowledge() only validates the delivery-ack token when durable credentials exist (refresh token, gateway key material, or project id). If the server's response omitted delivery_ack_token, the library refuses to proceed because it cannot prove delivery of the credentials to the server, which would otherwise let the server revoke them.

Source

Thrown at packages/device-auth/src/index.ts:57

}

async function acknowledge(
  options: {
    baseURL: string;
    client: string;
    code: DeviceCode;
    credentials: DeviceCredentials;
    fetcher: typeof globalThis.fetch;
    signal?: AbortSignal;
    sleep: (ms: number) => Promise<void>;
  },
): Promise<void> {
  const durable = Boolean(options.credentials.refresh_token || options.credentials.gateway_api_key ||
    options.credentials.gateway_key_id || options.credentials.project_id);
  if (!durable) return;
  const ackToken = options.credentials.delivery_ack_token;
  if (typeof ackToken !== "string" || ackToken === "") {
    throw new Error("device login failed: server did not provide a delivery acknowledgement token");
  }
  let lastError = "unknown error";
  for (let attempt = 0; attempt < 5; attempt++) {
    try {
      const response = await options.fetcher(`${options.baseURL}/api/v1/auth/device/ack`, {
        method: "POST",
        headers: {
          authorization: `Bearer ${options.credentials.access_token}`,
          "content-type": "application/json",
          "x-cave-client": options.client,
        },
        body: JSON.stringify({ device_code: options.code.device_code, ack_token: ackToken }),
        signal: requestSignal(options.signal, 5000),
      });
      if (response.ok) return;
      const body = await response.json().catch(() => null) as { error?: { code?: unknown } } | null;
      const code = typeof body?.error?.code === "string" ? body.error.code : `HTTP ${response.status}`;
      lastError = code;

View on GitHub (pinned to df2ccd85c9)

Solutions

  1. Check what the token endpoint returned (log/inspect the response JSON) — confirm whether delivery_ack_token is present.
  2. Upgrade or downgrade the device-auth client and server so both agree on the delivery_ack_token field.
  3. If running against a dev/mock server, make it return a delivery_ack_token in the credential response.
  4. Make credentials non-durable (no refresh_token/gateway_api_key/gateway_key_id/project_id) if you truly don't need durable delivery — acknowledge() then becomes a no-op.
  5. Ensure you don't overwrite credentials with a partial object that drops delivery_ack_token after login.

Example fix

// before (partial persist drops the token)
saveCredentials({ refresh_token: creds.refresh_token, project_id: creds.project_id });
// after
saveCredentials(creds); // keep delivery_ack_token alongside the durable fields
Defensive patterns

Strategy: validation

Validate before calling

const creds = loginResult.credentials;
const durable = Boolean(creds.refresh_token || creds.gateway_api_key || creds.gateway_key_id || creds.project_id);
if (durable && (typeof creds.delivery_ack_token !== "string" || creds.delivery_ack_token === ""))
  throw new Error("server response missing delivery_ack_token; upgrade client/server");

Type guard

function hasAckToken(c: Record<string, unknown>): c is { delivery_ack_token: string } & Record<string, unknown> {
  return typeof c.delivery_ack_token === "string" && c.delivery_ack_token !== "";
}

Try / catch

try {
  await acknowledge(options);
} catch (e) {
  if (e instanceof Error && e.message.includes("delivery acknowledgement token")) {
    console.error("Auth server did not return delivery_ack_token — check server/client versions or dev stub");
  } else throw e;
}

Prevention

When it happens

Trigger: Calling acknowledge() (directly or as part of the device flow) with durable credentials while options.credentials.delivery_ack_token is missing, an empty string, or not a string — i.e. the token-exchange server response did not include the field.

Common situations: Server/client version mismatch where the server no longer returns delivery_ack_token; a mock or stub auth server used in dev that omits the field; persisting credentials but dropping the token during JSON round-tripping; truncated API response fields.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@df2ccd85c9 (2026-08-31). Data as JSON: /api/errors/5a8cb199a656e44e. Report an issue: GitHub.