yikart/AiToEarn · error · AppException
InvalidWorkLink
InvalidWorkLink
Error message
InvalidWorkLink
What it means
getLinkInfo parses the supplied link with parseThreadsPostLink; if the string doesn't match a Threads post URL shape, parseThreadsPostLink returns null and InvalidWorkLink is thrown. No network call is involved — it's pure URL-shape validation.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/threads/threads-work.provider.ts:73
publishedAt: parsePlatformDate(post.timestamp),
authorName: post.username,
authorPlatformUid: post.owner?.id,
})),
pagination: {
mode: ChannelPaginationMode.Cursor,
nextCursor: response.paging?.cursors?.after,
previousCursor: response.paging?.cursors?.before,
hasNext: Boolean(response.paging?.next),
hasPrevious: Boolean(response.paging?.previous),
limit,
},
}
}
async getLinkInfo(input: WorkLinkInfoInput): Promise<ChannelWorkDataResult> {
const parsed = this.parseThreadsPostLink(input.link)
if (!parsed) {
throw new AppException(ResponseCode.InvalidWorkLink)
}
return {
snapshots: [],
work: {
id: parsed.postId,
url: parsed.normalizedUrl,
},
}
}
async getDetail(input: WorkDetailInput): Promise<ChannelWorkDataResult> {
const post = await this.threadsService.getPublishedPost(
input.platformWorkId,
input.credential.accessToken,
'id,status,permalink,text,timestamp,username',
)
const fetchedAt = new Date()
const timestamp = post.timestamp ? new Date(post.timestamp) : undefinedView on GitHub (pinned to d3aa8bea5b)
Solutions
- Confirm the input is a Threads post permalink containing a post id (threads.net/<@user>/post/<id>).
- Pre-validate the URL shape client-side before dispatching to the Threads provider.
- Check parseThreadsPostLink's accepted patterns; normalize short/embed/share URLs to the canonical post permalink form first.
- Route non-Threads links to the correct platform's work provider instead.
Example fix
// before
const info = await threadsWorkProvider.getLinkInfo({ link: raw })
// after
const isThreadsPost = /threads\.(net|com)\/[^/]+\/post\/[\w]+/.test(raw)
if (!isThreadsPost) throw new Error('Expected a Threads post permalink')
const info = await threadsWorkProvider.getLinkInfo({ link: raw }) Defensive patterns
Strategy: validation
Validate before calling
function isThreadsPostLink(link: string): boolean {
return /^https?:\/\/(?:www\.)?threads\.(?:net|com)\/[^/]+\/post\/[\w-]+/.test(link)
}
if (!isThreadsPostLink(link)) throw new Error('Expected a Threads post permalink (threads.net/<user>/post/<id>)') Type guard
function parseThreadsPostUrl(link: string): { postId: string } | null {
const m = link.match(/threads\.(?:net|com)\/[^/]+\/post\/([\w-]+)/)
return m ? { postId: m[1] } : null
} Try / catch
try {
await threadsWorkProvider.getLinkInfo({ link })
} catch (e) {
if (e instanceof AppException && e.code === ResponseCode.InvalidWorkLink) {
return { ok: false, reason: 'Not a Threads post permalink' }
}
throw e
} Prevention
- Validate the URL shape client-side before dispatching to a platform provider.
- Normalize share text and embed URLs into canonical post permalinks.
- Detect the platform from the hostname before choosing a provider.
- Keep the accepted-URL regex updated as Threads changes URL formats.
When it happens
Trigger: Calling threadsWorkProvider.getLinkInfo with a link that parseThreadsPostLink cannot parse — not a threads.net/@user/post/<id> style URL, malformed, or a different platform's URL.
Common situations: User pasted a Threads profile or share text instead of a post permalink; URL uses an unexpected locale subdomain or embed format; link is actually from Instagram/X but was routed to the Threads provider.
Related errors
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/e83f5c278c938da9.
Report an issue: GitHub.