Skip to content

Remove rewriteConfiguration from write path to fix config2.map crash#7131

Open
lizkenyon wants to merge 2 commits intomainfrom
03-30-fix_guard_rewriteconfiguration_against_non-array_and_nullish_config_values
Open

Remove rewriteConfiguration from write path to fix config2.map crash#7131
lizkenyon wants to merge 2 commits intomainfrom
03-30-fix_guard_rewriteconfiguration_against_non-array_and_nullish_config_values

Conversation

@lizkenyon
Copy link
Copy Markdown
Contributor

@lizkenyon lizkenyon commented Mar 30, 2026

Summary

Fixes shop/issues#8553config2.map is not a function
Vault issue: https://vault.shopify.io/issues/8435

Stops using rewriteConfiguration in writeAppConfigurationFile and replaces it with a simple stripEmptyObjects utility. The Zod schema walk was the root cause of the crash. rewriteConfiguration is kept exported for its other consumer (breakdown-extensions.ts), which is untouched in this PR.

Root Cause

When loadOpaqueApp (#6751) can't Zod-validate a local TOML config (e.g., a third-party template without client_id), it falls back to raw, unvalidated TOML data. This raw data is deep-merged with the remote config and passed to writeAppConfigurationFile, where rewriteConfiguration walked the Zod schema assuming config values matched schema types — calling .map() on non-arrays and Object.entries() on non-objects.

Triggered by app dev --reset (or any link flow) on apps using third-party templates.

Why remove instead of guard

  • Defensive guards are an infinite game of catching arbitrary type mismatches
  • Pass-through of invalid data causes downstream bugs (e.g., writing a config without client_id makes the app appear "unlinked")
  • rewriteConfiguration's only meaningful behaviors were key reordering (cosmetic) and empty-object stripping — only the latter is needed
  • A longer-term improvement would be to use TomlFile.patch() for surgical edits that preserve comments/formatting

Changes

  • write-app-configuration-file.ts: Stop calling rewriteConfiguration in writeAppConfigurationFile. Add stripEmptyObjects utility. Add structuredClone to prevent mutating the caller's config (previously masked by rewriteConfiguration creating fresh objects). Add Array.isArray guard in condenseComplianceAndNonComplianceWebhooks. Keep rewriteConfiguration exported for breakdown-extensions.ts.
  • link.ts: Remove schema from writeAppConfigurationFile call and getAppVersionedSchema import.
  • Tests: Update all callers. Add stripEmptyObjects tests. Add type-mismatched data crash test. Convert link.test.ts TOML comparisons to snapshots. Update round-trip stability tests to 3-write pattern. Update 2 configuration object expectations to reflect that write-time mutations (webhook condensation, URI relativization) no longer leak back to the caller.

Behavior change

Key ordering in the generated TOML now follows JS object insertion order rather than schema declaration order. This may cause a one-time key reorder in shopify.app.toml on the next app link or app config pull. Subsequent writes are stable.

Not changed

  • breakdown-extensions.ts — still uses rewriteConfiguration for config diffing. Its inputs come from the platform API and Zod-parsed config (not the raw TOML fallback), so it does not have the same crash risk.

Test plan

  • stripEmptyObjects unit tests
  • writeAppConfigurationFile tests (comments, empty entries, empty arrays, type-mismatched data)
  • Config pipeline snapshot tests regenerated
  • Link tests pass (23/23)
  • Breakdown extensions tests pass without changes (25/25)
  • Manual: shopify app init with a template lacking client_id, then shopify app dev --reset — should not crash

🤖 Generated with Claude Code

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@lizkenyon lizkenyon force-pushed the 03-30-fix_guard_rewriteconfiguration_against_non-array_and_nullish_config_values branch 2 times, most recently from 1efd2f7 to 0c746ca Compare April 1, 2026 15:37
@lizkenyon lizkenyon marked this pull request as ready for review April 1, 2026 15:44
@lizkenyon lizkenyon requested review from a team as code owners April 1, 2026 15:44
Copilot AI review requested due to automatic review settings April 1, 2026 15:44
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens rewriteConfiguration so it can safely reorder keys even when it receives nullish or type-mismatched config data (e.g., raw TOML fallback from third-party templates), preventing crashes like config2.map is not a function during app config writes.

Changes:

  • Add early null/undefined guards in rewriteConfiguration, plus Array.isArray and typeof === 'object' checks before array/object traversal.
  • Add unit tests covering null/undefined and type-mismatched values (including a case using the real buildVersionedAppSchema()).
  • Add a patch changeset documenting the crash fix.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
packages/app/src/cli/services/app/write-app-configuration-file.ts Adds defensive guards in rewriteConfiguration to avoid .map()/Object.entries() on invalid shapes.
packages/app/src/cli/services/app/write-app-configuration-file.test.ts Adds focused tests to ensure rewriteConfiguration doesn’t throw on nullish/type-mismatched config.
.changeset/chilly-glasses-fetch.md Records a patch release note for the crash fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@lizkenyon lizkenyon changed the title Fix: guard rewriteConfiguration against non-array and nullish config values Remove rewriteConfiguration to fix config2.map crash Apr 1, 2026
…sions

The Zod schema walk in rewriteConfiguration crashes when writeAppConfigurationFile
receives unvalidated data from loadOpaqueApp's raw TOML fallback (third-party
templates without client_id). Instead of adding defensive guards to the schema walk,
stop using it in the write path entirely.

- Replace rewriteConfiguration with stripEmptyObjects in writeAppConfigurationFile
- Add structuredClone to prevent mutating caller's config object
- Add Array.isArray guard in condenseComplianceAndNonComplianceWebhooks
- Keep rewriteConfiguration exported for breakdown-extensions.ts (unchanged)
- Drop schema parameter from writeAppConfigurationFile signature

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@lizkenyon lizkenyon force-pushed the 03-30-fix_guard_rewriteconfiguration_against_non-array_and_nullish_config_values branch from a304a30 to 59362c7 Compare April 1, 2026 21:12
@lizkenyon lizkenyon changed the title Remove rewriteConfiguration to fix config2.map crash Remove rewriteConfiguration from write path to fix config2.map crash Apr 1, 2026
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