yikart/AiToEarn · error · FacebookPlatformException

15070

15070

Error message

{{platform}} platform API request failed

What it means

FacebookService's createHttpClient installs a single axios response-rejection interceptor that converts every AxiosError on calls to the Facebook Graph API into a FacebookPlatformException (ChannelPlatformException) with message '{{platform}} platform API request failed' (code 15070). Unlike Bilibili/Douyin, there is no fromPlatformResponse interceptor here — non-2xx Graph API responses (Graph signals errors via HTTP status) are caught by this interceptor and wrapped with httpStatus, endpoint, and a classified category/retryable flag.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/facebook/facebook.service.ts:46

@Injectable()
export class FacebookService {
  private readonly logger = new Logger(FacebookService.name)
  private readonly http: AxiosInstance

  constructor(
    private readonly cfg: FacebookConfig,
    private readonly mediaService: MediaService,
  ) {
    this.http = this.createHttpClient()
  }

  private createHttpClient(): AxiosInstance {
    const http = axios.create()
    http.interceptors.response.use(
      response => response,
      (error: AxiosError<FacebookErrorBody>) => {
        throw FacebookPlatformException.fromAxiosError(error)
      },
    )
    return http
  }

  private get graphApiBaseUrl(): string {
    return `https://graph.facebook.com/${this.cfg.graphApiVersion}`
  }

  private get facebookDialogBaseUrl(): string {
    return `https://www.facebook.com/${this.cfg.graphApiVersion}`
  }

  generateAuthUrl(scopes: string[], state: string): string {
    const params = new URLSearchParams({
      client_id: this.cfg.clientId,
      redirect_uri: this.cfg.redirectUri,
      scope: scopes.join(','),

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Inspect exception.cause.httpStatus and platformCode from error.response.data.error: 190 = invalid/expired token → reconnect the Facebook account; 4/17/613 = rate limit → back off and retry later.
  2. If e.retryable is true (5xx or network-level failure), retry with exponential backoff; for 401/403 mark the channel as needing reauthorization instead of retrying.
  3. Use exception.context.endpoint (graphApiBaseUrl + path) to identify which Graph call failed and check required permissions/scopes for it.
  4. Verify FACEBOOK app credentials and that the server can reach graph.facebook.com (proxy/DNS/firewall) — reproduce with curl from the container.

Example fix

// before: generic catch hides the Graph error subcode
try {
  await facebookService.publish(page, post)
} catch (e) {
  this.logger.error('fb publish failed', e)
}

// after: branch on the classified platform exception
try {
  await facebookService.publish(page, post)
} catch (e) {
  if (e instanceof ChannelPlatformException) {
    if (e.cause?.httpStatus === 401 || e.cause?.platformCode === 190) {
      return this.channels.markNeedsReauth(page.accountId)
    }
    if (e.retryable) return this.retryWithBackoff(e)
  }
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: verify token and required permission before Graph calls
if (!account.facebookPageToken || account.facebookTokenExpiresAt < Date.now()) {
  throw new Error('Facebook token missing or expired; reconnect required')
}
if (!grantedScopes.includes('pages_manage_posts')) {
  throw new Error('Facebook account lacks pages_manage_posts permission')
}

Type guard

import { ChannelPlatformException } from '../channels/platforms/platforms.exception'

function isFacebookPlatformError(e: unknown): e is ChannelPlatformException {
  return e instanceof ChannelPlatformException
    && e.platform === 'facebook'
    && (e.cause?.platformCode !== undefined || typeof e.cause?.httpStatus === 'number')
}

Try / catch

try {
  return await facebookService.publish(page, post)
} catch (e) {
  if (isFacebookPlatformError(e)) {
    if (e.cause?.platformCode === 190 || e.cause?.httpStatus === 401) {
      return markAccountNeedsReauth(page.accountId)
    }
    if ([4, 17, 613].includes(Number(e.cause?.platformCode)) || e.retryable) {
      return retryWithBackoff(e)
    }
  }
  throw e
}

Prevention

When it happens

Trigger: Graph API calls from this client (token exchange, page publishing, media upload to graph.facebook.com) returning 400/401/403/429/5xx; long-lived page token expired or user revoked permissions; app-level rate limiting (code 4, 17) or ads-rate-limit (code 613); network-level failures (timeout, DNS, proxy) where error.response is undefined.

Common situations: Expired Facebook access tokens after the 60-day sliding window without reconnect; missing publish_pages/pages_manage_posts permissions after app review changes; Meta platform outages (5xx); hitting Graph API rate limits during bulk publishing; wrong FACEBOOK_APP_ID/SECRET or redirect URI producing 400 on OAuth token exchange.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/d8bdd4cf63dbc836. Report an issue: GitHub.