yikart/AiToEarn · error · YouTubePlatformException
15070
15070
Error message
{{platform}} platform API request failed What it means
YouTubeService installs a response interceptor in createHttpClient that converts every failed Axios response into YouTubePlatformException.fromAxiosError. Because the client is created in the constructor, any YouTube API call made through this instance that returns an HTTP error (or network failure) surfaces as code ChannelPlatformApiFailed (15070) with message '{{platform}} platform API request failed'. The underlying status, endpoint, and YouTube error body are preserved in the exception's cause.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/youtube/youtube.service.ts:32
import { YoutubeOAuthGrantType, YoutubeSearchOrder, YoutubeSearchType } from './youtube.schema'
@Injectable()
export class YoutubeService {
private readonly http: AxiosInstance
constructor(
private readonly cfg: YoutubeConfig,
private readonly mediaService: MediaService,
) {
this.http = this.createHttpClient()
}
private createHttpClient(): AxiosInstance {
const http = axios.create()
http.interceptors.response.use(
response => response,
(error: AxiosError<YouTubeErrorBody>) => {
throw YouTubePlatformException.fromAxiosError(error)
},
)
return http
}
private createOAuth2Client() {
return new google.auth.OAuth2(
this.cfg.clientId,
this.cfg.clientSecret,
this.cfg.redirectUri,
)
}
private createYouTubeClient(accessToken: string) {
const auth = new google.auth.OAuth2()
auth.setCredentials({ access_token: accessToken })
return google.youtube({ version: 'v3', auth })
}View on GitHub (pinned to d3aa8bea5b)
Solutions
- Read the exception cause (httpStatus, platformCode like quotaExceeded/invalidGrant) to identify the real failure.
- On 401, refresh the access token and retry once; refresh proactively before expiry based on expiresAt.
- On 403 quotaExceeded, back off and schedule work outside quota-heavy windows or request a quota increase.
- On 404, re-fetch the resource list and treat the item as gone rather than retrying.
- Retry only retryable statuses (429, 5xx) with exponential backoff; never retry 400/401/403 non-quota errors.
Example fix
// before
const res = await youtubeHttp.get('/youtube/v3/videos', { params })
// after: handle auth/quota explicitly
try {
const res = await youtubeHttp.get('/youtube/v3/videos', { params })
} catch (e) {
if (e.httpStatus === 401) return this.withRefreshedToken(() => fetchVideos(params))
if (e.cause?.platformCode === 'quotaExceeded') throw new QuotaExceededException()
throw e
} Defensive patterns
Strategy: type-guard
Validate before calling
function isYouTubeRequestSafe(accessToken: string, expiresAt?: Date): boolean {
return accessToken.length > 0 && (!expiresAt || expiresAt.getTime() > Date.now() + 60_000)
} Type guard
function isYouTubePlatformError(e: unknown): e is ChannelPlatformException & { cause: { httpStatus?: number; platformCode?: string } } {
return e instanceof ChannelPlatformException
&& e.code === ResponseCode.ChannelPlatformApiFailed
&& e.cause !== undefined
} Try / catch
try {
const res = await youtubeHttp.get('/youtube/v3/videos', { params })
return res.data
} catch (e) {
if (!isYouTubePlatformError(e)) throw e
const { httpStatus, platformCode } = e.cause
if (httpStatus === 401 || platformCode === 'invalidGrant') return refreshAndRetryOnce()
if (platformCode === 'quotaExceeded' || httpStatus === 429) throw new QuotaBackoffException(e)
if (httpStatus === 404) return null // resource gone
throw e
} Prevention
- Refresh YouTube tokens before expiry using expiresAt instead of reacting to 401s.
- Branch on the exception's httpStatus/platformCode — the generic message never tells you enough.
- Track quota usage (cost per videos.insert/upload is high) and spread heavy jobs across the day.
- Only retry 429/5xx with exponential backoff; treat 400/403(non-quota)/404 as terminal.
When it happens
Trigger: Any YouTube Data API request through this HttpClient returning non-2xx: expired/insufficient-scope access tokens (401/403 quotaExceeded/forbidden), quota exceeded (403), invalid video/resource IDs (404), malformed requests (400), rate limiting (429), or network-level Axios failures.
Common situations: YouTube OAuth tokens expiring without proactive refresh (401); daily API quota exhausted after heavy uploads (403 quotaExceeded); uploading videos longer than allowed for unverified apps; channel removed or video set private between listing and fetching; calling APIs without the right scope for private resources.
Related errors
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/78fb2e9c625d1e9a.
Report an issue: GitHub.