gitroomhq/postiz-app · error · Error

Missing external url

Error message

Missing external url

What it means

The mentions endpoint loads the integration by body.id for the current org; if no matching record exists, Error('Invalid integration') is thrown (surfacing as 500).

Source

Thrown at apps/backend/src/api/routes/integrations.controller.ts:226

        .getAllowedSocialsIntegrations()
        .includes(integration)
    ) {
      throw new Error('Integration not allowed');
    }

    // A provider migrated via MIGRATE_PROVIDERS reconnects through its target
    // provider's OAuth: the callback lands on the target and the channel is
    // migrated in place (see migrateIntegration).
    const migrateTo = refresh
      ? this._integrationManager.getMigrationTarget(integration)
      : undefined;

    const integrationProvider = this._integrationManager.getSocialIntegration(
      migrateTo || integration
    );

    if (integrationProvider.externalUrl && !externalUrl) {
      throw new Error('Missing external url');
    }

    try {
      const getExternalUrl = integrationProvider.externalUrl
        ? {
            ...(await integrationProvider.externalUrl(externalUrl)),
            instanceUrl: externalUrl,
          }
        : undefined;

      const { codeVerifier, state, url } =
        await integrationProvider.generateAuthUrl(getExternalUrl);

      if (refresh) {
        await ioRedis.set(`refresh:${state}`, refresh, 'EX', 3600);
      }

      if (onboarding === 'true') {

View on GitHub (pinned to 0f1647f749)

Solutions

  1. Reload the channel list and use a currently connected integration id
  2. Remove disconnected channels from any persisted drafts/schedules referencing them
  3. Verify the integration row exists for the active org

Example fix

// before
fetchMentions({ id: draft.integrationId });
// after
const exists = integrations.some(i => i.id === draft.integrationId);
if (exists) fetchMentions({ id: draft.integrationId });
else removeIntegrationFromDraft(draft.integrationId);
Defensive patterns

Strategy: validation

Validate before calling

const list = await getIntegrations();
if (!list.some(i => i.id === body.id)) throw new Error('Channel disconnected');

Type guard

const isConnected = (id: string, list: Integration[]) => list.some(i => i.id === id);

Try / catch

try { await mentions(body); } catch (e) { if (e.message === 'Invalid integration') refreshAndDrop(body.id); else throw e; }

Prevention

When it happens

Trigger: POST /integrations/mentions with an id for a channel that was disconnected, deleted, or belongs to a different organization.

Common situations: Composer still referencing a channel removed after a token expiry; multi-org tokens reused; stale client-side cache of integrations.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27). Data as JSON: /api/errors/16077a253b6ef1d7. Report an issue: GitHub.