{"record":{"id":"080cced49ca314c2","repo":"jackwener/OpenCLI","slug":"twitter-collection-unresolved-relationship","errorCode":"twitter_collection_unresolved_relationship","errorMessage":"twitter_collection_unresolved_relationship: repost target is unavailable","messagePattern":"twitter_collection_unresolved_relationship: repost target is unavailable","errorType":"error_code","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/collection.js","lineNumber":126,"sourceCode":"        : contextStatus;\n    return {\n        post_id: postId,\n        author_handle: authorHandle,\n        author_id: authorId,\n        url: postId && authorHandle ? `https://x.com/${authorHandle}/status/${postId}` : null,\n        context_status: resolvedContextStatus,\n    };\n}\n\nfunction extractRelationship(result) {\n    const tweet = unwrapTweetResult(result);\n    const legacy = tweet?.legacy || {};\n    const repostResult = tweet?.retweeted_status_result?.result || legacy.retweeted_status_result?.result || null;\n    const repostId = legacy.retweeted_status_id_str || null;\n    if (repostResult || repostId) {\n        const target = relationshipTarget(repostResult, repostId, repostResult ? 'complete' : 'unknown');\n        if (!repostResult || !target.post_id) {\n            throw new CommandExecutionError('twitter_collection_unresolved_relationship: repost target is unavailable');\n        }\n        return { kind: 'repost', target };\n    }\n    const quoteResult = tweet?.quoted_status_result?.result || legacy.quoted_status_result?.result || null;\n    const quoteId = legacy.quoted_status_id_str || null;\n    if (legacy.is_quote_status || quoteResult || quoteId) {\n        return {\n            kind: 'quote',\n            target: relationshipTarget(quoteResult, quoteId, quoteResult ? 'complete' : 'unavailable'),\n        };\n    }\n    const replyId = legacy.in_reply_to_status_id_str || null;\n    const replyHandle = normalizeTwitterScreenName(legacy.in_reply_to_screen_name || '') || null;\n    const replyAuthorId = typeof legacy.in_reply_to_user_id_str === 'string' && legacy.in_reply_to_user_id_str.trim()\n        ? legacy.in_reply_to_user_id_str\n        : null;\n    if (replyId || replyHandle || replyAuthorId) {\n        return {","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/collection.js#L108-L144","documentation":"When a collected timeline post is a repost, extractRelationship requires the resolved repost target (retweeted_status_result.result) and a concrete post_id. If only an ID stub exists without resolvable content, or the target can't be resolved to a post_id, the library throws rather than emitting a repost entry with an unusable target.","triggerScenarios":"A tweet has legacy.retweeted_status_id_str or a retweeted_status_result but the embedded result is missing/tombstoned (deleted or withheld repost target), so target.post_id cannot be built.","commonSituations":"Archiving timelines where the reposted tweet was deleted or made unavailable after the repost; withheld/geo-blocked content returning tombstone results; X GraphQL payloads omitting retweeted_status_result for older reposts.","solutions":["Re-run collection — tombstones are sometimes transient; retry after a delay","Skip or log-and-continue for that tweet if your tooling allows (the error is per-post)","Check whether the target tweet is deleted/withheld by fetching its ID directly","Update the CLI — newer X API shapes may need a newer unwrap path"],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"function repostTargetAvailable(tweet) {\n  const legacy = tweet?.legacy || {};\n  const result = tweet?.retweeted_status_result?.result || legacy.retweeted_status_result?.result;\n  const id = legacy.retweeted_status_id_str;\n  return Boolean(result && result?.rest_id) || Boolean(!result && id); // stub-only still fails downstream\n}","typeGuard":"function hasResolvableRepost(tweet) {\n  const r = tweet?.retweeted_status_result?.result ?? tweet?.legacy?.retweeted_status_result?.result;\n  return r != null && typeof r.rest_id === 'string';\n}","tryCatchPattern":"try {\n  posts.push(extractCollectionPost(entry, seen));\n} catch (err) {\n  if (String(err.message).startsWith('twitter_collection_unresolved_relationship')) {\n    console.warn('skipping post with unavailable repost target');\n  } else throw err;\n}","preventionTips":["Expect tombstones for deleted/withheld repost targets and handle them per-post","Re-run collections to catch transiently unavailable targets","Fetch repost target IDs directly to confirm availability before archiving","Keep the CLI updated for current retweeted_status_result shapes"],"tags":["twitter","repost","data-integrity","protocol"],"backgroundTag":"unresolved-relationship-target","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}