{"record":{"id":"cf15d0e313356144","repo":"yikart/AiToEarn","slug":"15070-cf15d0","errorCode":"15070","errorMessage":"{{platform}} platform API request failed","messagePattern":"(.+?)\\} platform API request failed","errorType":"exception","errorClass":"InstagramPlatformException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/instagram/instagram.service.ts","lineNumber":38,"sourceCode":"import { InstagramConfig } from './instagram.config'\nimport { InstagramPlatformException } from './instagram.exception'\nimport { InstagramOAuthGrantType } from './instagram.interface'\nimport { InstagramMediaContainerStatusResponseSchema, InstagramMediaType } from './instagram.schema'\n\n@Injectable()\nexport class InstagramService {\n  private readonly http: AxiosInstance\n\n  constructor(private readonly cfg: InstagramConfig) {\n    this.http = this.createHttpClient()\n  }\n\n  private createHttpClient(): AxiosInstance {\n    const http = axios.create()\n    http.interceptors.response.use(\n      response => response,\n      (error: AxiosError<InstagramErrorBody>) => {\n        throw InstagramPlatformException.fromAxiosError(error)\n      },\n    )\n    return http\n  }\n\n  private get graphApiBaseUrl(): string {\n    return `https://graph.instagram.com/${this.cfg.graphApiVersion}`\n  }\n\n  generateAuthUrl(scopes: string[], state: string): string {\n    const params = new URLSearchParams({\n      client_id: this.cfg.clientId,\n      redirect_uri: this.cfg.redirectUri,\n      scope: scopes.join(','),\n      state,\n      response_type: 'code',\n    })\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/instagram/instagram.service.ts#L20-L56","documentation":"InstagramService's axios interceptor converts every failed Graph API request into an InstagramPlatformException with ResponseCode.ChannelPlatformApiFailed (15070). Network-level Axios failures (no response) and non-2xx responses from graph.facebook.com (or the configured graphApiBaseUrl) are both wrapped, with the Instagram error body parsed for platform code/message. The exception records endpoint, method, HTTP status, category, retryability, and the raw platform payload.","triggerScenarios":"Any request through this.http — publishing media, fetching Instagram business account info, querying insights, exchanging tokens — that returns a non-2xx Graph API response (invalid OAuth token, missing instagram_business_account, permission denied, rate limit, invalid media params) or fails at the network level (timeout, DNS, connection refused).","commonSituations":"Facebook/Instagram access token expired or app-scoped token lacking instagram_basic/instagram_content_publish permissions; account not converted to an Instagram Business/Creator account and not linked to a Facebook Page; Graph API version deprecation after version changes; Meta rate limiting (code 4, 613) during bulk posting; egress blocked to graph.facebook.com.","solutions":["Inspect the thrown InstagramPlatformException's cause (platformCode, platformMessage, httpStatus) to get Meta's specific error subcode and message.","If httpStatus is 401/190 (invalid token), re-authenticate the channel or refresh the long-lived token before retrying.","If the error indicates missing permission (code 200/102), re-run OAuth with instagram_basic, instagram_content_publish, pages_show_list scopes and re-link the account.","If retryable (429/5xx/network), back off and retry; Meta rate limits require waiting per X-Business-Use-Case-Usage headers.","Confirm the target account is an Instagram Business/Creator account linked to a Facebook Page, and that the configured Graph API version is still supported."],"exampleFix":"// before: expired token causes 190 error wrapped as 15070\nconst res = await this.http.get(`${this.graphApiBaseUrl}/${igUserId}?fields=followers_count`, { params: { access_token: token } })\n\n// after: detect token failure and refresh\ntry {\n  const res = await this.http.get(`${this.graphApiBaseUrl}/${igUserId}`, { params: { fields: 'followers_count', access_token: token } })\n} catch (e) {\n  if (e instanceof InstagramPlatformException && e.cause?.platformCode === 190) {\n    token = await this.refreshLongLivedToken(channel)\n    // retry request with new token\n  } else throw e\n}","handlingStrategy":"try-catch","validationCode":"// pre-flight: token present, not expired, and account is a linked IG business account\nif (!accessToken || tokenExpiresAt <= new Date()) await refreshLongLivedToken(channel)\nif (!igUserId) throw new Error('Channel has no linked instagram_business_account; re-link via OAuth')","typeGuard":"function isInstagramPlatformException(e: unknown): e is InstagramPlatformException {\n  return e instanceof InstagramPlatformException\n}","tryCatchPattern":"try {\n  const result = await instagramService.publish(accessToken, igUserId, payload)\n} catch (e) {\n  if (e instanceof InstagramPlatformException) {\n    if (e.cause?.platformCode === 190 || e.cause?.httpStatus === 401) {\n      // refresh token and retry once\n    } else if (e.retryable) {\n      // backoff retry (429 / 5xx / network)\n    } else {\n      // surface cause.platformMessage to the channel owner\n    }\n  } else throw e\n}","preventionTips":["Keep Meta long-lived tokens fresh and re-authenticate channels before token expiry (~60 days).","Ensure the Instagram account is Business/Creator type and linked to a Facebook Page before connecting the channel.","Request all needed scopes (instagram_basic, instagram_content_publish, pages_show_list) during OAuth to avoid 403 permission errors.","Track Meta BUC usage headers and throttle bulk publishing to avoid 429 rate limits.","Pin and periodically review the Graph API version your service targets; deprecations cause sudden failures."],"tags":["instagram","facebook-graph-api","axios-interceptor","http-error","oauth"],"backgroundTag":"platform-api-request-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}