calcom/cal.diy · warning · Error

Already installed

Error message

Already installed

What it means

Thrown by the Giphy install handler when a `credential` row of type `giphy_other` already exists for the same owner (user or team). It is a plain `new Error(...)`, so `getServerErrorFromUnknown` routes it through `getHttpStatusCode`, whose switch falls through to the `default` branch because "Already installed" is not a known ErrorCode — yielding HTTP 500, not the 409 Conflict the message implies. The client therefore sees a 500 with body `{ message: "Already installed" }` for an entirely expected duplicate-install condition.

Source

Thrown at packages/app-store/giphy/api/add.ts:34

    return res.status(401).json({ message: "You must be logged in to do this" });
  }

  const userId = req.session.user.id;
  const appType = "giphy_other";
  const teamId = Number(req.query.teamId);
  const credentialOwner = req.query.teamId ? { teamId } : { userId: req.session.user.id };

  await throwIfNotHaveAdminAccessToTeam({ teamId: teamId ?? null, userId });

  try {
    const alreadyInstalled = await prisma.credential.findFirst({
      where: {
        type: appType,
        ...credentialOwner,
      },
    });
    if (alreadyInstalled) {
      throw new Error("Already installed");
    }
    const installation = await prisma.credential.create({
      data: {
        type: appType,
        key: {},
        ...credentialOwner,
        appId: "giphy",
      },
    });
    if (!installation) {
      throw new Error("Unable to create user credential for giphy");
    }
  } catch (error: unknown) {
    const httpError = getServerErrorFromUnknown(error);
    return res.status(httpError.statusCode).json({ message: httpError.message });
  }

  return res.status(200).json({ url: getInstalledAppPath({ variant: "other", slug: "giphy" }) });

View on GitHub (pinned to 176037d0af)

Solutions

  1. Fix the source: throw `new HttpError({ statusCode: 409, message: "Already installed" })` so the client gets a correct, handlable status instead of a misleading 500.
  2. Make the client idempotent: query installed apps before calling install and skip when Giphy is already present.
  3. Debounce/disable the Install button after the first click to prevent double-submit.
  4. On the client, treat a response whose message is "Already installed" as a no-op success and proceed to the installed-apps view.

Example fix

// before
if (alreadyInstalled) {
  throw new Error("Already installed");
}
// after
import { HttpError } from "@calcom/lib/http-error";
if (alreadyInstalled) {
  throw new HttpError({ statusCode: 409, message: "Already installed" });
}
Defensive patterns

Strategy: validation

Validate before calling

// Before calling the Giphy install endpoint, confirm it isn't already installed
import prisma from "@calcom/prisma";

async function ensureGiphyNotInstalled(owner: { userId: number } | { teamId: number }) {
  const existing = await prisma.credential.findFirst({
    where: { type: "giphy_other", ...owner },
    select: { id: true },
  });
  return existing === null; // true => safe to install
}

// usage:
if (await ensureGiphyNotInstalled({ userId })) {
  await fetch("/api/integrations/giphy/add", { method: "POST" });
}

Try / catch

// Treat the (currently 500) 'Already installed' response as a benign no-op
try {
  const res = await fetch("/api/integrations/giphy/add", { method: "POST" });
  if (!res.ok) throw new Error(await res.text());
} catch (e) {
  if (/Already installed/.test(String(e))) {
    // already installed — proceed to the installed-apps view
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: A request to the Giphy install endpoint (`/api/integrations/giphy/add`, with or without `?teamId=`) when `prisma.credential.findFirst` already returns a `giphy_other` credential for that `userId`/`teamId`. Concretely: double-clicking Install, resubmitting after a first successful install, two concurrent requests both passing the `findFirst` check before either `create`s.

Common situations: User clicks Install twice before navigating away; page refresh resubmits the install form; team admin re-installs for a team that already has Giphy; race condition between two near-simultaneous install requests.

Related errors


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