---
title: Source maps
description: Resolve minified storefront errors to private original source with a verified release workflow.
slug: source-maps
---

# Resolve errors to original source

Dozenfold can map a minified Shopify JavaScript frame back to its original file, line, and column.
Private source maps are attached to one shop and one immutable release. They are never served to the
storefront or exposed through a public URL.

## Choose the setup path

Use **CI automation** for normal deployments. Use **manual upload** to prove one already-published
Shopify build or when the deployment pipeline cannot run the CLI yet.

Both paths require the exact release configured in the Dozenfold storefront SDK. Do not reuse a
release identifier for a different build.

## Automate every deployment

1. Open **Releases → Source maps** as a shop Owner.
2. Create a CI credential and copy the token immediately. Dozenfold shows it only once.
3. Add it to the merchant repository as the masked `DOZENFOLD_SOURCE_MAP_TOKEN` secret.
4. Install an exact CLI version in the storefront project.
5. Finish compilation/minification, inject a debug ID before publishing, upload the matching map,
   publish that exact bundle, verify the deployed CDN bytes, then mark the release.

```bash
npm install --save-dev --save-exact @dozenfold/cli@0.2.0
npx dozenfold source-maps inject --manifest dozenfold-source-maps.json
npx dozenfold source-maps upload \
  --manifest dozenfold-source-maps.json \
  --shop example.myshopify.com
# Publish the exact injected bundle here.
npx dozenfold source-maps verify \
  --manifest dozenfold-source-maps.json \
  --shop example.myshopify.com \
  --cdn
npx dozenfold releases create \
  --release shopify-theme-2026-09-03.1 \
  --shop example.myshopify.com \
  --note "Deploy $GITHUB_SHA"
```

`releases create` records the deployment in [Releases](/docs/releases) and tags new storefront
events with that release, so the before/after comparison starts at the deploy.

### Use the GitHub Action

The [Dozenfold GitHub Action](https://github.com/marketplace/actions/dozenfold-source-maps) runs the
same commands:

```yaml
- run: npx dozenfold source-maps inject --manifest dozenfold-source-maps.json
- uses: Dozenfold/cli@v1
  with:
    manifest: dozenfold-source-maps.json
    shop: example.myshopify.com
    token: ${{ secrets.DOZENFOLD_SOURCE_MAP_TOKEN }}
# Publish the exact injected bundle here.
- uses: Dozenfold/cli@v1
  with:
    command: verify
    verify-cdn: 'true'
    manifest: dozenfold-source-maps.json
    shop: example.myshopify.com
    token: ${{ secrets.DOZENFOLD_SOURCE_MAP_TOKEN }}
- uses: Dozenfold/cli@v1
  with:
    command: release
    release: shopify-theme-2026-09-03.1
    note: Deploy ${{ github.sha }}
    shop: example.myshopify.com
    token: ${{ secrets.DOZENFOLD_SOURCE_MAP_TOKEN }}
```

Pass the token through the environment for upload and verification. Never put it in the manifest or
on the command line.

```yaml
env:
  DOZENFOLD_SOURCE_MAP_TOKEN: ${{ secrets.DOZENFOLD_SOURCE_MAP_TOKEN }}
```

The manifest binds local build files to the release and public asset URL:

```json
{
  "version": 1,
  "release": "shopify-theme-2026-09-03.1",
  "artifacts": [
    {
      "minified_url": "https://example.myshopify.com/cdn/shop/t/7/assets/theme.min.js?v=123",
      "minified_file": "dist/theme.min.js",
      "source_map": "dist/theme.min.js.map"
    }
  ]
}
```

Paths are relative to the manifest. Use the URL served by Shopify rather than a local path or an
assumed CDN URL. The `?v` selector is used to verify the deployed bytes; artifact identity is the
canonical URL without query or fragment.

## Keep Shopify's published bundle matched to its map

A source map translates positions in **one specific generated JavaScript file** back to original
source. It cannot automatically account for another transformation after it was generated.
If a publisher moves code from line 32 to line 1, the earlier map still describes the earlier file.

Shopify can [automatically minify JavaScript theme assets](https://shopify.dev/docs/storefronts/themes/best-practices/performance/platform).
For a build that is already prepared,
publish it as a `.min.js` theme asset and update the Liquid reference and manifest accordingly.
In our development-store rehearsal this preserved the injected bundle; **still verify the actual
CDN bytes** with `source-maps verify --cdn` on every deployment. The filename is not a substitute
for verification.

Use this order:

1. Complete the final build/minification and generate its corresponding source map.
2. Run `source-maps inject` on that final bundle/map pair.
3. Publish the injected bundle unchanged and upload its matching private map. Do not minify,
   concatenate, prepend a banner or otherwise rewrite the JavaScript after injection.
4. Put the exact published asset URL, including Shopify's current `?v` selector, in the manifest.
   Run `source-maps verify --cdn`; treat a failure as an incomplete source-map setup.
5. Confirm an actual captured occurrence opens the expected original line in Developer.

If verification reports a digest mismatch, compare the local injected bundle with the file served
by the CDN. Remove the extra transformation or generate the map for the final transformed output,
then prepare a new immutable release. Do not copy an old debug ID onto changed bytes or reuse the
old map. Keep `.map` files private; only the JavaScript asset belongs in the theme.

If manual upload reports a missing debug ID even though injection ran, check whether publication
removed the injected marker. Repeating the upload cannot repair a map for different JavaScript.
The CLI's `--cdn` check compares the local build bytes with the served bytes; do not omit it from
the deployment acceptance step.

### Why a debug ID does not repair a changed file

A debug ID identifies the bundle/map pair; it does not describe a later transformation. This is
also the distinction in [Sentry's debug-ID workflow](https://docs.sentry.io/platforms/javascript/sourcemaps/troubleshooting_js/debug-ids/).
Its [open-source CLI](https://github.com/getsentry/sentry-cli/blob/master/src/utils/sourcemaps.rs)
adjusts source-map positions for the JavaScript injection it performs itself. That adjustment
does not account for a publisher subsequently minifying the file again. Keep the final bundle
and its map together, then verify what the browser actually receives.

## Upload one Shopify build manually

Open **Releases → Source maps → Manual upload** after the asset is published. Enter the release,
paste the exact Shopify JavaScript asset URL, and choose its matching `.map` file. Dozenfold fetches
the published bundle, checks its debug ID against the map, records the fetched bundle's digest,
and verifies the private artifact by reading it back. Manual upload does not have your local
JavaScript file to compare; use the CLI's `--cdn` check when verifying local-versus-published bytes.

Manual upload does not modify a bundle. If the published asset does not contain a Dozenfold debug
ID, rebuild with `source-maps inject` before publishing it.

## Confirm that mapping works

Open an issue captured from that release and select **Developer**. A successful match shows
**Source-map evidence: Ready**, the captured minified frame, and the resolved original frame. A
limited source excerpt appears only when the uploaded map contains `sourcesContent`.

These states require different actions:

- **Not configured:** no matching private artifact exists for the shop, release, and script URL.
- **Bundle and map do not match:** the deployed bundle and uploaded map came from different builds.
- **Unavailable:** verification could not complete; retry without changing the release identity.

## Rotate access safely

Credentials are shop-scoped and can only upload maps, read their verification status and mark
releases. Credentials created before October 4, 2026 cannot mark releases; create a new one and
revoke the old one. Use a
separate credential per deployment environment, prefer a 90-day expiry, rotate before expiry, and
revoke a token immediately when a repository or CI environment is no longer trusted.

Maps and embedded `sourcesContent` stay in private tenant-isolated storage. Default retention is 180
days; public ingest tokens, storefront code, and other shops cannot read them.

## Current limits

The manual map limit is 5 MiB and the verified JavaScript asset limit is 50 MiB. A manifest may
contain up to 500 artifacts. This release supports regular source-map v3 files; indexed maps must be
flattened or split during the build.
