calcom/cal.diy · error · Error

app_ticket invalid, please try again

Error message

app_ticket invalid, please try again

What it means

Thrown by getAppAccessToken when Feishu returns code 10012 on the app_access_token request, indicating the supplied app_ticket is invalid (typically outdated). The handler first clears the stored app_ticket in the DB (so the next call will fetch a fresh one) and then throws so the caller retries.

Source

Thrown at packages/app-store/feishucalendar/lib/AppAccessToken.ts:140

      body: JSON.stringify({
        app_id: appKeys.app_id,
        app_secret: appKeys.app_secret,
        app_ticket,
      }),
    });

  const resp = await fetchAppAccessToken(appTicket);
  const data = await resp.json();

  if (!resp.ok || data.code !== 0) {
    logger.error("feishu error with error: ", data, ", logid is:", resp.headers.get("X-Tt-Logid"));
    // appticket invalid, mostly outdated, delete and renew one
    if (data.code === 10012) {
      await prisma.app.update({
        where: { slug: "feishu-calendar" },
        data: { keys: { ...appKeys, app_ticket: "" } },
      });
      throw new Error("app_ticket invalid, please try again");
    }
  }

  const newAppAccessToken = data.app_access_token;
  const newExpireDate = Math.round(Number(new Date()) / 1000 + data.expire);

  await prisma.app.update({
    where: { slug: "feishu-calendar" },
    data: {
      keys: {
        ...appKeys,
        app_access_token: newAppAccessToken,
        expire_date: newExpireDate,
      },
    },
  });

  return newAppAccessToken;

View on GitHub (pinned to 176037d0af)

Solutions

  1. Retry the operation: the handler already cleared the bad ticket, so the next getAppAccessToken triggers getAppTicket which requests a fresh one from Feishu.
  2. Ensure the app_ticket webhook is functional (see error 552) so a new ticket is delivered.
  3. Verify app_id and app_secret in the Feishu app keys are correct.
  4. Wrap getAppAccessToken callers in a single-retry loop keyed on this error message.

Example fix

// before - single call, throws on 10012
const token = await getAppAccessToken();

// after - one automatic retry after ticket reset
async function getAppAccessTokenWithRetry() {
  try {
    return await getAppAccessToken();
  } catch (err) {
    if (err instanceof Error && err.message.includes("app_ticket invalid")) {
      return await getAppAccessToken();
    }
    throw err;
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure stored ticket is fresh before requesting access token
const keys = await getAppKeys();
if (keys.app_ticket && Date.now() - (keys.app_ticket_received_at ?? 0) > 60 * 60 * 1000) {
  await prisma.app.update({ where: { slug: "feishu-calendar" }, data: { keys: { ...keys, app_ticket: "" } } });
}

Type guard

const isAppAccessTokenResp = (v: any): boolean => typeof v?.code === "number";

Try / catch

async function getAppAccessTokenWithRetry() {
  try { return await getAppAccessToken(); }
  catch (err) {
    if (err instanceof Error && /app_ticket invalid/i.test(err.message)) return await getAppAccessToken();
    throw err;
  }
}

Prevention

When it happens

Trigger: Feishu POST /open-apis/auth/v3/app_access_token returns code === 10012 because the app_ticket stored in DB is stale, expired (Feishu tickets live ~1 hour), or was issued for a different app_id.

Common situations: Stale app_ticket persisted from a previous app configuration; clock skew; Feishu rotated tickets but the webhook that updates DB failed; app_id/app_secret mismatch between Cal.com keys and the Feishu app.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/1841822c8d121e26. Report an issue: GitHub.