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) : undefined

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Confirm the input is a Threads post permalink containing a post id (threads.net/<@user>/post/<id>).
  2. Pre-validate the URL shape client-side before dispatching to the Threads provider.
  3. Check parseThreadsPostLink's accepted patterns; normalize short/embed/share URLs to the canonical post permalink form first.
  4. 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

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.