`og:title` mapping silently uses `<meta name="title">` when it appears before `<meta property="og:title">`
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
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:
- https://github.com/giscus/giscus/blob/3d6430237108ca4ee3eb6a1a20595201c09c72d5/client.ts#L63-L64 —
params.term = getMetaContent('title', true) - https://github.com/giscus/giscus/blob/3d6430237108ca4ee3eb6a1a20595201c09c72d5/client.ts#L53 —
params.description = getMetaContent('description', true)(same defect,meta[name="description"]can shadowog:description)
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:
<!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):
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:
<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:
- https://github.com/XhstormR/blog/discussions/5 —
Vibe Coding(created before the theme switch) - https://github.com/XhstormR/blog/discussions/9 —
Vibe Coding · XhstormR's Blog(created after)
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:
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:
data-mapping="specific"
data-term="{{ .Title }}"Related (not duplicates)
- #738 — duplicate discussions with
data-strict, but withdata-mapping="pathname"; a different cause (no meta-tag resolution involved). - #601 —
og:titlepartial vs exact matching; about the search semantics, not about which meta tag is read.
Environment
- giscus:
https://giscus.app/client.jsserved on 2026-08-19 (bug also present inclient.tsatmain, commit3d64302) - Reproduced in Chrome (any browser is affected — the cause is spec-defined
querySelectorbehavior) - Site generator: Hugo + Blowfish theme (any page ordering
meta[name=title]beforemeta[property=og:title]is affected)
Source: giscus/giscus