{"record":{"id":"16077a253b6ef1d7","repo":"gitroomhq/postiz-app","slug":"missing-external-url","errorCode":null,"errorMessage":"Missing external url","messagePattern":"Missing external url","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/backend/src/api/routes/integrations.controller.ts","lineNumber":226,"sourceCode":"        .getAllowedSocialsIntegrations()\n        .includes(integration)\n    ) {\n      throw new Error('Integration not allowed');\n    }\n\n    // A provider migrated via MIGRATE_PROVIDERS reconnects through its target\n    // provider's OAuth: the callback lands on the target and the channel is\n    // migrated in place (see migrateIntegration).\n    const migrateTo = refresh\n      ? this._integrationManager.getMigrationTarget(integration)\n      : undefined;\n\n    const integrationProvider = this._integrationManager.getSocialIntegration(\n      migrateTo || integration\n    );\n\n    if (integrationProvider.externalUrl && !externalUrl) {\n      throw new Error('Missing external url');\n    }\n\n    try {\n      const getExternalUrl = integrationProvider.externalUrl\n        ? {\n            ...(await integrationProvider.externalUrl(externalUrl)),\n            instanceUrl: externalUrl,\n          }\n        : undefined;\n\n      const { codeVerifier, state, url } =\n        await integrationProvider.generateAuthUrl(getExternalUrl);\n\n      if (refresh) {\n        await ioRedis.set(`refresh:${state}`, refresh, 'EX', 3600);\n      }\n\n      if (onboarding === 'true') {","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/gitroomhq/postiz-app/blob/0f1647f7491a217d43eb5ae7a480484bdf0aff3e/apps/backend/src/api/routes/integrations.controller.ts#L208-L244","documentation":"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).","triggerScenarios":"POST /integrations/mentions with an id for a channel that was disconnected, deleted, or belongs to a different organization.","commonSituations":"Composer still referencing a channel removed after a token expiry; multi-org tokens reused; stale client-side cache of integrations.","solutions":["Reload the channel list and use a currently connected integration id","Remove disconnected channels from any persisted drafts/schedules referencing them","Verify the integration row exists for the active org"],"exampleFix":"// before\nfetchMentions({ id: draft.integrationId });\n// after\nconst exists = integrations.some(i => i.id === draft.integrationId);\nif (exists) fetchMentions({ id: draft.integrationId });\nelse removeIntegrationFromDraft(draft.integrationId);","handlingStrategy":"validation","validationCode":"const list = await getIntegrations();\nif (!list.some(i => i.id === body.id)) throw new Error('Channel disconnected');","typeGuard":"const isConnected = (id: string, list: Integration[]) => list.some(i => i.id === id);","tryCatchPattern":"try { await mentions(body); } catch (e) { if (e.message === 'Invalid integration') refreshAndDrop(body.id); else throw e; }","preventionTips":["Clear mentions caches when a channel disconnects","Validate ids from drafts against the live channel list before dispatch"],"tags":["integrations","mentions","not-found","stale-id"],"backgroundTag":"entity-not-found","analyzedSha":"0f1647f7491a217d43eb5ae7a480484bdf0aff3e","analyzedAt":"2026-08-27T12:09:55.020Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}