{"record":{"id":"e61b150ec61bc981","repo":"forem/forem","slug":"invalid-stack-exchange-site-tag-input","errorCode":null,"errorMessage":"Invalid Stack Exchange site: {% %{tag} %{input} %}","messagePattern":"Invalid Stack Exchange site: (.+?) %(.+?) %\\}","errorType":"exception","errorClass":"StandardError","httpStatus":null,"severity":"error","filePath":"app/liquid_tags/stackexchange_tag.rb","lineNumber":54,"sourceCode":"        created_at: @json_content[\"creation_date\"],\n        score: @json_content[\"score\"],\n        comment_count: @json_content[\"comment_count\"],\n        answer_count: @json_content[\"answer_count\"],\n        post_type: @post_type,\n        post_url: @json_content[\"link\"] || default_link,\n        body: @json_content[\"body\"]\n      },\n    )\n  end\n\n  private\n\n  def parse_site(input)\n    return \"stackoverflow\" if tag_name == \"stackoverflow\" || input.match?(STACKOVERFLOW_REGEXP)\n\n    match = pattern_match_for(input, REGEXP_OPTIONS)\n    # rubocop:disable Layout/LineLength\n    raise StandardError, I18n.t(\"liquid_tags.stackexchange_tag.invalid_site\", tag: tag_name, input: input) unless match && match_group_present?(match, \"subdomain\")\n    # rubocop:enable Layout/LineLength\n\n    match[:subdomain].downcase\n  end\n\n  def match_group_present?(match, group_name)\n    match.names.include?(group_name)\n  end\n\n  def get_data(input)\n    id = input.split.first\n    match = pattern_match_for(id, REGEXP_OPTIONS)\n    unless match && match_group_present?(match, \"id\")\n      raise StandardError, I18n.t(\"liquid_tags.stackexchange_tag.invalid_id\", tag: tag_name, input: input)\n    end\n\n    url = \"#{API_URL}posts/#{match[:id]}?site=#{@site}&filter=#{FILTERS['post']}\" \\\n          \"&key=#{ApplicationConfig['STACK_EXCHANGE_APP_KEY']}\"","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/forem/forem/blob/f354c376a7c5d1330dc40d66f150be8d1289020d/app/liquid_tags/stackexchange_tag.rb#L36-L72","documentation":"Forem's {% stackexchange %} tag resolves the target site before fetching data: if the tag name is 'stackoverflow' or the input matches the stackoverflow.com post URL pattern, the site is 'stackoverflow'; otherwise the input must match one of the tag's regexps AND expose a named 'subdomain' capture group, or the tag raises 'Invalid Stack Exchange site' with the tag name and raw input interpolated. Bare numeric ids match SITE_REGEXP (?<subdomain>\\b[a-zA-Z]+\\b) only when letters are present, so a bare id with the stackexchange tag fails here, not at the id check.","triggerScenarios":"{% stackexchange 12345 %} (digits contain no letters, so no subdomain group), {% stackexchange https://stackoverflow.com/questions/123 %} with a URL shape that misses STACKOVERFLOW_REGEXP (e.g. wrong path segment like /q/ without digits or /documentation/), or any non-URL, non-site string raise at app/liquid_tags/stackexchange_tag.rb:54.","commonSituations":"Using the generic stackexchange tag with a bare numeric post id (the tag cannot infer the site from digits alone); typos in the site subdomain; URLs from meta.* or area51 that do not fit the (?<subdomain>\\w+\\.)?stackexchange\\.com pattern with a captured group.","solutions":["With a bare id, name the site explicitly via URL or use the subdomain form: {% stackexchange https://crypto.stackexchange.com/q/12345 %}.","For stackoverflow.com posts, use {% stackoverflow https://stackoverflow.com/q/12345 %} or the bare id with the stackoverflow tag name.","Match the URL shape: <subdomain>.stackexchange.com/(q|a|questions)/<digits>."],"exampleFix":"// before\n{% stackexchange 12345 %}\n\n// after\n{% stackexchange https://crypto.stackexchange.com/q/12345 %}","handlingStrategy":"validation","validationCode":"SE_SITE = %r{https://(?<subdomain>\\w+)\\.(?:stackexchange\\.com|stackoverflow\\.com)/(?:q|a|questions)/\\d{1,20}}\ndef stackexchange_site_resolvable?(input)\n  return true if input.match?(SE_SITE)\n  input.match?(STACKOVERFLOW ||= %r{https://stackoverflow\\.com/(q|a|questions)/\\d{1,20}}) || input =~ /\\b[a-zA-Z]+\b/\nend","typeGuard":null,"tryCatchPattern":"begin\n  Liquid::Template.parse(markdown).render\nrescue StandardError => e\n  show_editor_error(e.message) # interpolates tag name and raw input\nend","preventionTips":["Never pair a bare numeric id with the generic stackexchange tag — the site is unresolvable from digits.","For stackoverflow.com content use the stackoverflow tag name or full URL.","Keep the URL shape <subdomain>.stackexchange.com/(q|a|questions)/<digits>."],"tags":["liquid","stackexchange","stackoverflow","forem","url-validation","site-resolution"],"backgroundTag":"liquid-tag-validation-error","analyzedSha":"f354c376a7c5d1330dc40d66f150be8d1289020d","analyzedAt":"2026-08-21T12:43:44.428Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}