Skip to content

WIP: Fix remote-only configuration specs rejected as unsupported TOML sections#7399

Open
rogeryen wants to merge 1 commit intomainfrom
fix-contract-based-config-spec-validation
Open

WIP: Fix remote-only configuration specs rejected as unsupported TOML sections#7399
rogeryen wants to merge 1 commit intomainfrom
fix-contract-based-config-spec-validation

Conversation

@rogeryen
Copy link
Copy Markdown
Contributor

@rogeryen rogeryen commented Apr 24, 2026

Reported in: https://shopify.slack.com/archives/C07UJ7UNMTK/p1777047053015119

What

Fixes a bug where remote-only configuration specs (like purchase_options) with JSON schema contracts were incorrectly rejected with Unsupported section(s) in app configuration when present in shopify.app.toml.

Three changes across the config parsing pipeline:

  1. json-schema.ts — When validating a configuration spec in strip mode, the parser now scopes the data to the TOML section contents before running JSON schema validation, instead of passing the entire app config.
  2. specification.tscontributeToAppConfigurationSchema now handles contract-based config specs (where the schema is zod.any()) by contributing the spec identifier as a known top-level key.
  3. loader.ts — The loader now claims the spec identifier as a used key when it exists in the app configuration, as a safety net for contract-based specs whose parsed result keys don't include the section name.

Why

Remote-only configuration specs (specs returned by the server with experience: 'configuration' but no local definition) are created with zod.any() as their schema. This caused a data scoping mismatch during TOML validation:

  1. zod.any() passes the entire app config through to JSON schema validation.
  2. The JSON schema contract describes the section contents (e.g. {bundles: boolean} for purchase_options), not the whole config.
  3. AJV's strip mode removes all top-level keys that don't match the schema properties — but none of the app config's top-level keys (client_id, name, webhooks, purchase_options, etc.) match the section's properties (bundles).
  4. The result is {} — empty. No extension instance is created, no keys are claimed, and the TOML section is flagged as "unsupported".

This was proven empirically with debug logging on the unfixed code:

[3] Top-level keys passed to AJV: ["client_id","build","name","webhooks","access_scopes","auth","application_url","embedded","product","metaobjects","purchase_options"]
[4] JSON schema properties: ["type","handle","uid","path","extensions","bundles"]
[7] Does "bundles" key exist at top level? false
[9] AJV result data keys: []
[10] AJV result data: {}
[11] specConfiguration keys: []
[13] Is empty? true

The fix scopes validation to the section contents ({bundles: true}) rather than the whole config, and ensures the section key is properly claimed.

Testing instructions

To reproduce the original bug and verify the fix end-to-end:

  1. Set up: Make sure you have a Shopify app with a shopify.app.toml that includes a [purchase_options] section (or any other remote-only configuration spec section):

    [purchase_options]
    bundles = true
  2. Run from this branch: The CLI monorepo uses pnpm as its package manager. From the repo root, you can run CLI commands against an app directory using:

    dev cd cli
    gt get
    dev up
    pnpm shopify app deploy --path /path/to/your/app

    This will build the CLI from source (via nx build cli) and then run the command. The first run takes ~30s for the build; subsequent runs are faster if cached.

    You can also use pnpm shopify app dev --path /path/to/your/app to verify the section is accepted during development mode.

  3. Expected results:

    • Before this fix: The command fails with Unsupported section(s) in app configuration: purchase_options.
    • After this fix: The command proceeds past config validation. For deploy, it completes successfully (assuming valid app credentials and --allow-updates if running non-interactively).

Note: If you see stale build results, clear the build cache with npx nx reset && rm -rf packages/app/dist packages/app/tsconfig.build.tsbuildinfo before re-running.

Test coverage

  • json-schema.test.ts — 3 new tests: section scoping when identifier matches, no scoping when absent, validation errors propagated when scoped.
  • specification.integration.test.ts — 3 new tests: contributeToAppConfigurationSchema for contract-based config specs, extension specs (no contribution), and locally-defined config specs.
  • loader.test.ts — 1 new test: end-to-end loading a TOML with a contract-based config section verifying no "unsupported section" error.

@rogeryen rogeryen requested a review from a team as a code owner April 24, 2026 18:49
@rogeryen rogeryen changed the title Fix remote-only configuration specs rejected as unsupported TOML sections WIP: Fix remote-only configuration specs rejected as unsupported TOML sections Apr 24, 2026
Copy link
Copy Markdown
Contributor

@shopeter shopeter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎩 'd locally and it worked! TY!

@rogeryen rogeryen force-pushed the fix-contract-based-config-spec-validation branch from b8361fc to 5026b9e Compare April 24, 2026 21:51
@rogeryen rogeryen force-pushed the fix-contract-based-config-spec-validation branch from 5026b9e to b4d412c Compare April 24, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants