Troubleshooting

The silent failures come first: no error, no log line, no exception — just an answer that looks plausible. They are the expensive ones.

Domain resolution

Symptom Likely cause
A site exists with the right domain, but getSite returns 404 Most often the site was filed inside a folder. Resolution only walks the top level of the workspace — a folder is not mgnl:content and is never descended into — so the site is invisible to getSite, to GET /v1/sites and to the MCP list. Move it back to the top level.
getSite returns 404 and the site is at the top level The pattern does not match the whole value. *.brand.com does not match the bare brand.com; use *brand.com, or add the bare domain as a second row.
It resolves in production but not in local development The value compared is exactly what the caller sends. When the front end runs on a non-standard port it sends the host with the port (localhost:3000), which a pattern of localhost alone cannot match in full. Add a row that covers it.
The wrong brand is served after a new site was added Order decides: sites are checked in the order shown in the app, and the first one with a matching row wins. A catch-all row (*) on an early site claims everything. Reorder before touching the patterns.
One site never matches, no matter what Its pattern is invalid as a regular expression. It is caught, logged as Invalid domain pattern '…', and treated as no match, so the site stays silently unreachable while everything else works. Check the log for that line.
?rootWebsite=… returns 404 for a page that exists The comparison is exact after normalisation (trim, leading /, no trailing /) — not a prefix match. /h1 does not resolve a site whose root is /h1/es or /h1-shop.
Resolution broke after the site's root page was moved or renamed rootWebsite stores the page's path, not a reference to it. Moving or renaming the page leaves the stored path pointing nowhere; re-pick the page in the site form.

The response

Symptom Likely cause
The response has no domains It never does. SiteFullDto does not expose the patterns, so they cannot be read back over REST at all. Read them in AdminCentral or through the MCP tool.
A site's social profiles vanished after an update A PUT that omits socialMedia deletes the whole container — an empty or absent list removes it. Read the site, send its rows back with the change, or use the MCP tool, which merges onto the stored item.
A PUT cleared a field you did not send It did not — absent and null scalars are left untouched. What clears a scalar is sending it as "". If a value disappeared, something sent an empty string.
Two rows for the same platform became one The rows are matched to the stored ones by provider, so a write through REST or MCP keeps only one child per platform and deletes the rest. The form allows the duplicate; the write path does not preserve it.
POST fails and no site is created The body has no nodeName. REST does not derive it from name — that is the MCP tool's behaviour, not the endpoint's.
name and description come back in the wrong language They are localised from Accept-Language, matched against the locales configured in Magnolia. A language with no translation falls back to the default rather than returning empty, so "wrong language" usually means the translation was never filled in.
Everything is empty on public and fine on author The anonymous user has no read access to h1-sites. It never surfaces as an error: the list is [], getSite is 404, and h1-seo drops the site globals from its structured data. Suspect the ACL before the code.

SEO and integrations

Symptom Likely cause
Structured data has no organization, no social profiles and no fallback social image h1-seo reads them from h1-sites in the request's own session, and treats the whole thing as optional: if the module is absent or the workspace unreadable, it omits them rather than failing. Check the anonymous ACL on h1-sites.
h1-seo finds no site for a brand It matches a site by its rootWebsite against /{brand}. A brand with no site is not an error there, so the globals are simply missing. Check that a site exists whose root page is exactly /{brand}.
The site MCP tool refuses to write mdTheme By design. mdTheme belongs to frontend-md3, and a tool does not write another module's fields. The rejection names its owner: use the md_theme_decoration_on_site tool.
There is no site MCP tool at all h1-mcp-api is not installed, so there is no registry, nobody reads h1McpTools/, and the tool stays inert. The rest of the module is unaffected.
A site moved into a folder disappeared from the MCP list Same cause as the resolution case above. The tool inherits create_folder and move from its base class, but sites only work at the top level.