Skip to content

Conversation

@dmerand
Copy link
Contributor

@dmerand dmerand commented Jan 28, 2026

Summary

Implements client-side detection and handling for multi-user dev session conflicts, building on backend changes in https://github.com/shop/world/pull/383392.

When multiple developers run shopify app dev on the same app/shop combination, the CLI now provides clear feedback to both users about the session takeover and gracefully handles the conflict.

Changes

GraphQL Query Updates

  • dev-session-create.graphql: Now requests devSession, warnings, and userErrors fields
  • dev-session-update.graphql: Now requests devSession with user and websocket information

Schema Updates

Updated local schema file (app_dev_schema.graphql) to match backend changes:

  • Added devSession field to DevSessionCreatePayload
  • Added warnings array to DevSessionCreatePayload
  • Added devSession field to DevSessionUpdatePayload
  • Added new types: DevSessionWarning and DevSessionWarningCode enum

Implementation (dev-session.ts)

Session State Tracking:

  • Added DevSessionState interface to track websocket URL and user information
  • Added currentSessionState instance variable to maintain session metadata

Create-Time Warning Display:

  • Stores initial session state when dev session is created
  • Displays warnings from backend (e.g., SESSION_TAKEOVER)
  • Warnings are non-blocking - session creation succeeds even with warnings

Update-Time Takeover Detection:

  • Compares returned websocketUrl against expected local websocket URL on every update
  • If mismatch detected → another developer has taken over the session
  • Displays clear error message including the new user's email
  • Throws AbortError to gracefully terminate the dev session with actionable next steps

User Experience

Scenario: User B Takes Over User A's Session

User A starts dev:

$ shopify app dev
✅ Ready, watching for changes in your app

User B starts dev (same app/shop):

$ shopify app dev
⚠️  Another user is already running a dev preview for this app on example.myshopify.com.
✅ Ready, watching for changes in your app

User A makes a code change:

[app-extension] Extension updated

❌ Error
└  
⚠️  Another developer (user-b@shopify.com) has taken over this dev session.
Your preview is no longer active. Terminating dev session...

Dev session has been taken over by another user.
You can restart by running \`shopify app dev\` again.

Benefits

  • Immediate feedback for aggressor: User B knows they displaced someone
  • Quick discovery for victim: User A learns about takeover on next code change (typically seconds)
  • Clean separation: Warnings (non-blocking) vs errors (blocking)
  • Graceful termination: Clear messaging with actionable next steps
  • No false positives: Only checks during actual mutations, not on normal disconnects
  • Future-proof: Uses structured warning codes for extensibility

Dependencies

This PR depends on backend changes:

Testing Plan

Once the backend PR is merged:

  1. Test session takeover warning appears when User B starts dev
  2. Test User A's session terminates gracefully on next update after takeover
  3. Verify error messages are clear and actionable
  4. Test edge cases:
    • Same user, multiple machines
    • Same user, same machine (should show no warning)
    • Rapid takeover scenarios

Addresses https://github.com/shop/issues-develop/issues/21606

Measuring impact

  • n/a - this doesn't need measurement, e.g. a linting rule or a bug-fix

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes

Copy link
Contributor Author

dmerand commented Jan 28, 2026

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

@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Coverage report

St.
Category Percentage Covered / Total
🟡 Statements 79.65% 14494/18196
🟡 Branches 73.94% 7196/9732
🟡 Functions 79.81% 3691/4625
🟢 Lines 80.01% 13704/17128

Test suite run success

3721 tests passing in 1438 suites.

Report generated by 🧪jest coverage report action from e448893

@craigmichaelmartin craigmichaelmartin force-pushed the dlm-session-takeover branch 8 times, most recently from d1954be to 3389ecf Compare February 9, 2026 19:15
@craigmichaelmartin craigmichaelmartin marked this pull request as ready for review February 9, 2026 20:17
@craigmichaelmartin craigmichaelmartin requested a review from a team as a code owner February 9, 2026 20:17
@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

We detected some changes at packages/*/src and there are no updates in the .changeset.
If the changes are user-facing, run pnpm changeset add to track your changes and include them in the next release CHANGELOG.

Caution

DO NOT create changesets for features which you do not wish to be included in the public changelog of the next CLI release.

@craigmichaelmartin
Copy link
Contributor

I left a comment in slack that I'm surprised were doing this validation logic regarding dev preview takeover for updates in the CLI (vs the DevApi)? I expected the latter (server validation vs client validation reasons; + it would make tuning this logic/copy easier in the future since CLI is versioned) but stuck with how it was for now.

@craigmichaelmartin craigmichaelmartin force-pushed the dlm-session-takeover branch 3 times, most recently from ea7bd9d to fa92fff Compare February 9, 2026 21:04
@gonzaloriestra
Copy link
Contributor

/snapit

@github-actions
Copy link
Contributor

🫰✨ Thanks @gonzaloriestra! Your snapshot has been published to npm.

Test the snapshot by installing your package globally:

npm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20260210131100

Caution

After installing, validate the version by running just shopify in your terminal.
If the versions don't match, you might have multiple global instances installed.
Use which shopify to find out which one you are running and uninstall it.

Copy link
Contributor

@gonzaloriestra gonzaloriestra left a comment

Choose a reason for hiding this comment

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

Working great! Comments are not blocking

Comment on lines +421 to +423
await this.logger.error(
`Another developer ${newUserEmail ? `(${newUserEmail}) ` : ''}has taken ownership of this dev preview.`,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why showing the error twice? I'd get rid of this if we are going to abort.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's nice to see something in the log, even if we end up saying the same thing in the final error message. I asked about copy for these messages here and while I didn't explicitly ask if both were warranted, there wasn't push back against having both.

Comment on lines +426 to +428
const message = `Another user${
newUserEmail ? ` (${newUserEmail})` : ''
} has taken ownership of this dev preview. Your preview is no longer active.`
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a big deal, but the issue I mentioned here is not fixed.

If the only change is the websocket URL, we are saying Another user, but showing the same email.

if (warnings.length > 0) {
await Promise.all(
warnings.map((warning) => {
const message = warning.code === 'SESSION_TAKEOVER' ? `⚠️ ${warning.message}` : warning.message
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not just adding the emoji to all the warnings?

Suggested change
const message = warning.code === 'SESSION_TAKEOVER' ? `⚠️ ${warning.message}` : warning.message
const message = `⚠️ ${warning.message}`

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.

3 participants