#1727·giscus

`og:title` mapping silently uses `<meta name="title">` when it appears before `<meta property="og:title">`

Author: XhstormRCreated Aug 18, 2026Updated Aug 18, 2026

Summary

With data-mapping="og:title", giscus does not reliably read <meta property="og:title">. getMetaContent() passes a comma-separated selector list to document.querySelector(), which returns the first element in tree order that matches any selector in the list — not the first selector that matches. So on any page where <meta name="title"> appears before <meta property="og:title"> in the DOM, giscus uses the name="title" value as the search term.

This contradicts the documented behavior (locales/en/config.json):

titleContainsOgTitleDesc: giscus will search for a discussion whose title contains the page's <meta property="og:title"> HTML tag.

Practical consequence: the term differs from the og:title the site author configured, so giscus fails to find the existing discussion and creates a duplicate discussion for the same page.

Affected code

https://github.com/giscus/giscus/blob/3d6430237108ca4ee3eb6a1a20595201c09c72d5/client.ts#L10-L17

typescript
function getMetaContent(property: string, og = false) {
  const ogSelector = og ? `meta[property='og:${property}'],` : '';
  const element = document.querySelector<HTMLMetaElement>(
    ogSelector + `meta[name='${property}']`,
  );

  return element ? element.content : '';
}

Callers that pass og = true:

Per the DOM specification, querySelector returns "the first element that is a descendant of node that matches selectors" in tree order — selector order inside the list carries no precedence: https://dom.spec.whatwg.org/#dom-parentnode-queryselector

Steps to reproduce

Minimal page:

xml
<!doctype html>
<html>
  <head>
    <!-- note the order: name="title" comes first -->
    <meta name="title" content="My Post · My Site" />
    <meta property="og:title" content="My Post" />
  </head>
  <body>
    <script
      src="https://giscus.app/client.js"
      data-repo="OWNER/REPO"
      data-repo-id="..."
      data-category="Announcements"
      data-category-id="..."
      data-mapping="og:title"
      data-strict="1"
      data-emit-metadata="1"
      crossorigin="anonymous"
      async
    ></script>
  </body>
</html>

Or, without any giscus setup, run this in the console of such a page — it reproduces the exact selector used by getMetaContent('title', true):

javascript
document.querySelector("meta[property='og:title'],meta[name='title']").content;
// => "My Post · My Site"   (expected: "My Post")

Expected

giscus uses the term My Post (the og:title value) and resolves the discussion titled My Post.

Actual

giscus uses the term My Post · My Site, finds no matching discussion, and creates a new one titled My Post · My Site.

Real-world impact

This is not a synthetic ordering. The Hugo theme Blowfish emits, in this order:

xml
<title>Vibe Coding · XhstormR's Blog</title>
<meta name="title" content="Vibe Coding · XhstormR's Blog" />
...
<meta property="og:title" content="Vibe Coding" />

Live page: https://xhstormr.github.io/blog/posts/vibe-coding/ (og:title is Vibe Coding)

The previous theme (Hugo PaperMod) does not emit <meta name="title">, so the same site with the same unchanged giscus config produced the term Vibe Coding. After switching themes, giscus started using Vibe Coding · XhstormR's Blog, and every commented page ended up with two discussions for one URL:

Because the failure is invisible in the widget (giscus simply shows an empty thread and creates a new discussion on the first comment), comment history looks silently lost.

Proposed fix

Query the og: selector and the name= selector separately so precedence is explicit rather than dependent on document order:

typescript
function getMetaContent(property: string, og = false) {
  const element =
    (og
      ? document.querySelector<HTMLMetaElement>(`meta[property='og:${property}']`)
      : null) ?? document.querySelector<HTMLMetaElement>(`meta[name='${property}']`);

  return element ? element.content : '';
}

This keeps the existing name= fallback (needed for pages that only expose <meta name="...">) while guaranteeing that og:* wins when both are present, which is what the og:title mapping and the docs promise.

Compatibility note

Sites that today accidentally match on <meta name="title"> / <meta name="description"> (because those tags precede the og:* tags) would see their term change after this fix, i.e. giscus would stop finding the discussions it previously created for those pages. That is the same class of breakage the bug already causes, but it would be worth calling out in the release notes, and it may argue for landing the fix together with a documentation note. Happy to open a PR if the approach looks right.

Workaround for site authors

Do not rely on og:title; inject the term explicitly from the static site generator:

xml
data-mapping="specific"
data-term="{{ .Title }}"

Related (not duplicates)

  • #738 — duplicate discussions with data-strict, but with data-mapping="pathname"; a different cause (no meta-tag resolution involved).
  • #601 — og:title partial vs exact matching; about the search semantics, not about which meta tag is read.

Environment

  • giscus: https://giscus.app/client.js served on 2026-08-19 (bug also present in client.ts at main, commit 3d64302)
  • Reproduced in Chrome (any browser is affected — the cause is spec-defined querySelector behavior)
  • Site generator: Hugo + Blowfish theme (any page ordering meta[name=title] before meta[property=og:title] is affected)