OrphanedSnippet: contextual templates (*.context.*.json) crash theme graph, flagging every snippet as orphaned
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 234
- Forks
- 92
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 6
Description
Description
A theme containing a contextual template (templates/*.context.*.json) causes buildThemeGraph to throw. themeCheckRun swallows that error in a bare catch, leaving themeGraph undefined, so getReferences() returns [] for every file and OrphanedSnippet reports every snippet in the theme as orphaned.
On a real theme this meant 152 of 153 snippets were flagged (the only exception being one snippet excluded via a top-level ignore), including snippets rendered directly by sections.
The failure is silent — nothing in the CLI output indicates the graph failed to build.
Root cause
Contextual templates use parent plus partial section overrides, so their section entries legitimately have no type (it is inherited from the parent template):
{
"context": { "market": "ca" },
"parent": "index.json",
"sections": { "hero": { "settings": {} } }
}
traverseJsonModule assumes every section in a JSON template has a type and dereferences it with a non-null assertion:
https://github.com/Shopify/theme-tools/blob/main/packages/theme-graph/src/graph/traverse.ts
const typeProperty = node.children.find((child) => child.key.value === 'type')!;
const start = typeProperty.loc.start.offset;
This is inconsistent with the schema-based traversal functions in the same file, which all guard correctly:
const typeProperty = node.children.find((child) => child.key.value === 'type');
if (!typeProperty) continue;
Four sites use the unguarded non-null assertion:
traverseJsonModule—JsonModuleKind.TemplatetraverseJsonModule—JsonModuleKind.SectionGrouptraverseSectionReferencestraverseBlockReferences
Contextual section groups (sections/*.context.*.json) have the same shape and hit the latter sites, including nested blocks that carry only settings.
Reproduction
Minimal theme:
templates/index.json
{ "sections": { "hero": { "type": "hero" } }, "order": ["hero"] }
templates/index.context.ca.json
{ "context": { "market": "ca" }, "parent": "index.json", "sections": { "hero": { "settings": {} } } }
sections/hero.liquid
{% render 'greeting' %}
{% schema %}
{ "name": "Hero", "settings": [] }
{% endschema %}
snippets/greeting.liquid
<p>Hello</p>
Result with @shopify/theme-check-node@3.28.1:
=== WITH contextual template present ===
total offenses: 1
OrphanedSnippet: 1 [ 'greeting.liquid' ]
=== WITHOUT contextual template ===
total offenses: 0
OrphanedSnippet: 0 []
snippets/greeting.liquid is rendered by sections/hero.liquid, so it should never be reported.
Calling buildThemeGraph directly, outside the try/catch, shows the underlying error:
TypeError: Cannot read properties of undefined (reading 'loc')
at traverseJsonModule (node_modules/@shopify/theme-graph/dist/graph/traverse.js:330:44)
at async Promise.all (index 1)
at async buildThemeGraph (node_modules/@shopify/theme-graph/dist/graph/build.js:33:5)
Impact
OrphanedSnippetis unusable on any theme using contextual templates or contextual section groups, which is common for themes serving multiple markets.- Every other cross-file check silently degrades, since
getReferences/getDependenciesreturn empty arrays whenever graph construction fails. - Adding the contextual templates to
ignorein.theme-check.ymlis not a workaround — graph traversal walks the templates and sections directories viadeps.fsand never consults the ignore config. Verified: offense count stayed at 153 after ignoring the files.
Suggested fix
Guard the four JSON traversal sites the same way the schema traversal functions already do (skip the reference when there is no type property), so contextual overrides contribute no edge rather than throwing.
Separately, it may be worth logging when buildThemeGraph fails in themeCheckRun rather than discarding the error, since "graceful degradation" currently turns into confidently wrong results for every reference-based check.
Versions
@shopify/cli4.6.1@shopify/theme-check-node3.28.1@shopify/theme-graph0.3.1- Also present on
mainat time of writing - Node 24.16.0, macOS
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/theme-graph/src/graph/traverse.ts and inspect the four JSON traversal sites that assume a section has a type, then review buildThemeGraph and themeCheckRun to understand the silent failure. Reproduce the contextual-template example and verify that graph construction succeeds and OrphanedSnippet no longer flags the rendered snippet.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100