yikart/AiToEarn · error · AppException

InvalidWorkLink

InvalidWorkLink

Error message

InvalidWorkLink

What it means

getWorkLinkInfo parses the RedNote URL to extract a noteId, optionally falling back to the dataId argument. If neither parseRedNoteUrl yields a noteId nor a dataId is supplied, the link cannot be identified and InvalidWorkLink is thrown.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/rednote/rednote-work.provider.ts:32

  workStatus?: WorkStatus
} & Record<string, string | PublishType | WorkStatus | undefined>

export type RedNoteWorkLinkInfoResult = ChannelWorkDataResult<RedNoteWorkLinkExtra> & { extra: RedNoteWorkLinkExtra }

@Injectable()
export class RedNoteWorkProvider implements WorkProvider {
  readonly requiresCredentialForLinkInfo = false
  private readonly logger = new Logger(RedNoteWorkProvider.name)

  async getLinkInfo(input: WorkLinkInfoInput): Promise<ChannelWorkDataResult> {
    return this.getWorkLinkInfo(input.link, input.dataId)
  }

  async getWorkLinkInfo(workLink: string, dataId?: string): Promise<RedNoteWorkLinkInfoResult> {
    const parsed = await this.parseRedNoteUrl(workLink)
    const resolvedDataId = parsed.noteId || dataId || ''
    if (!resolvedDataId) {
      throw new AppException(ResponseCode.InvalidWorkLink)
    }

    const resolvedUrl = parsed.resolvedUrl ?? workLink
    const extra: RedNoteWorkLinkExtra = {
      dataId: resolvedDataId,
      uniqueId: `${AccountType.RedNote}_${resolvedDataId}`,
      type: PublishType.VIDEO,
      videoType: 'short',
      resolvedUrl,
      originalWorkLink: resolvedUrl !== workLink ? workLink : undefined,
      workStatus: parsed.noteId && !parsed.xsecToken ? WorkStatus.LINK_ERROR : undefined,
    }

    return {
      work: {
        id: resolvedDataId,
        url: resolvedUrl,
        mediaType: PublishType.VIDEO,

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Verify the input is a direct RedNote note URL containing a note id (e.g. xiaohongshu.com/explore/<id> or /discovery/item/<id>).
  2. If you have the note id from another source, pass it as the dataId argument so the parse result isn't required.
  3. Inspect parseRedNoteUrl's accepted URL patterns and normalize the user-supplied share text/short link to a supported format before calling.
  4. Ask the user to re-copy the share link via the RedNote app's 'copy link' action.

Example fix

// before
const info = await workProvider.getWorkLinkInfo(userInput)
// after
const noteId = userInput.match(/(?:explore|discovery\/item)\/([0-9a-f]+)/)?.[1]
if (!noteId) throw new Error('Not a RedNote note link')
const info = await workProvider.getWorkLinkInfo(userInput, noteId)
Defensive patterns

Strategy: validation

Validate before calling

const NOTE_ID_RE = /(?:explore|discovery\/item)\/([0-9a-zA-Z]+)/
function isRedNoteNoteLink(link: string): boolean {
  return NOTE_ID_RE.test(link)
}
if (!isRedNoteNoteLink(link) && !dataId) throw new Error('Provide a RedNote note URL or a dataId')

Type guard

function isRedNoteNoteLink(link: string): link is string {
  return /xiaohongshu\.com\/.+(?:explore|discovery\/item)\/[0-9a-zA-Z]+/.test(link)
}

Try / catch

try {
  await workProvider.getWorkLinkInfo(link)
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.InvalidWorkLink) {
    return { ok: false, reason: 'URL is not a RedNote note link and no dataId was provided' }
  }
  throw e
}

Prevention

When it happens

Trigger: Calling getWorkLinkInfo (directly or via getLinkInfo) with a string that is not a recognizable RedNote note URL (no note id in path/query), and no dataId fallback passed.

Common situations: User pasted a RedNote homepage/profile/search URL instead of a note link; short-link or share-text format that parseRedNoteUrl doesn't handle; link to a deleted/private note that returns no id; mobile share URL variant not matching the parse regex.

Related errors


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