{"record":{"id":"730d25dfd0be6a87","repo":"forem/forem","slug":"invalid-twitter-timeline-url","errorCode":null,"errorMessage":"Invalid Twitter Timeline URL","messagePattern":"Invalid Twitter Timeline URL","errorType":"exception","errorClass":"StandardError","httpStatus":null,"severity":"error","filePath":"app/liquid_tags/twitter_timeline_tag.rb","lineNumber":44,"sourceCode":"        href: @href\n      },\n    )\n  end\n\n  private\n\n  def parse_link(link)\n    href = ActionController::Base.helpers.strip_tags(link).strip\n    raise_error unless valid_link?(href)\n    href\n  end\n\n  def valid_link?(link)\n    link.match?(URL_REGEXP)\n  end\n\n  def raise_error\n    raise StandardError, I18n.t(\"liquid_tags.twitter_timeline_tag.invalid_url\")\n  end\nend\n\nLiquid::Template.register_tag(\"twitter_timeline\", TwitterTimelineTag)\n\nUnifiedEmbed.register(TwitterTimelineTag, regexp: TwitterTimelineTag::REGISTRY_REGEXP)\n","sourceCodeStart":26,"sourceCodeEnd":51,"githubUrl":"https://github.com/forem/forem/blob/f354c376a7c5d1330dc40d66f150be8d1289020d/app/liquid_tags/twitter_timeline_tag.rb#L26-L51","documentation":"TwitterTimelineTag validates input against the strictly anchored URL_REGEXP: https scheme, twitter.com host, an alphanumeric-only handle, the literal /timelines/ path, and a numeric timeline id, with nothing before or after. Any mismatch raises StandardError 'Invalid Twitter Timeline URL'.","triggerScenarios":"Using an x.com timeline URL; a handle containing '_' (only [a-zA-Z0-9] is allowed); a trailing slash, query string, or fragment after the id; an http:// URL; a plain profile URL like https://twitter.com/username.","commonSituations":"Twitter's rebrand making x.com the host people copy from; underscores in handles (very common); share URLs appending tracking params (?s=20) that break the end anchor.","solutions":["Use the exact form https://twitter.com/<handle>/timelines/<numeric_id>","Rewrite x.com hosts back to twitter.com before embedding","Strip query strings, fragments, and trailing slashes from the URL","If handles with underscores must work, extend the character class in both URL_REGEXP and REGISTRY_REGEXP ([a-zA-Z0-9_]) in your fork and add specs"],"exampleFix":"# before\n{% twitter_timeline https://x.com/dev_rel/timelines/1234567890?s=20 %} # wrong host + query string\n\n# after\n{% twitter_timeline https://twitter.com/dev_rel/timelines/1234567890 %}","handlingStrategy":"validation","validationCode":"href = input.strip\nraise ArgumentError, \"bad timeline URL\" unless href.match?(TwitterTimelineTag::URL_REGEXP)","typeGuard":"def valid_timeline_url?(url)\n  url.match?(TwitterTimelineTag::URL_REGEXP) # anchored: exact twitter.com/<handle>/timelines/<id> shape\nend","tryCatchPattern":"begin\n  Liquid::Template.parse(\"{% twitter_timeline #{href} %}\")\nrescue StandardError => e\n  raise unless e.message == I18n.t(\"liquid_tags.twitter_timeline_tag.invalid_url\")\n  # normalize: rewrite x.com -> twitter.com, strip params, retry once\nend","preventionTips":["Canonicalize to https://twitter.com/<handle>/timelines/<id> before saving","Strip query strings and fragments from copied links","Remember underscores in handles fail the pattern - verify with a regex match in a pre-save lint"],"tags":["liquid-tag","forem","twitter","timeline","url-validation"],"backgroundTag":"invalid-embed-url","analyzedSha":"f354c376a7c5d1330dc40d66f150be8d1289020d","analyzedAt":"2026-08-21T12:43:44.428Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}