Skip to content

🐛 Fixed "labels is not defined" error when creating comped members via API#26837

Merged
troyciesco merged 4 commits intomainfrom
ONC-1553_comped-api-err
Mar 19, 2026
Merged

🐛 Fixed "labels is not defined" error when creating comped members via API#26837
troyciesco merged 4 commits intomainfrom
ONC-1553_comped-api-err

Conversation

@troyciesco
Copy link
Contributor

closes https://linear.app/tryghost/issue/ONC-1553

  • The add() method in memberBREADService passed the full options object (including withRelated: ['labels', 'newsletters']) to setComplimentarySubscription().
  • When the Stripe customer had existing subscriptions, fetching related stripeSubscriptions with those options caused a "labels is not defined on the model" error on StripeCustomerSubscription models.
  • This fix uses sharedOptions (context + transacting only), matching the pattern already used for linkStripeCustomer().

Summary

When creating a comped member via the Admin API with a stripe_customer_id pointing to a Stripe customer that has existing subscriptions, setComplimentarySubscription() received the full options object including withRelated: ['labels', 'newsletters']. Inside that method, member.related('stripeSubscriptions').fetch(options) attempted to eager-load labels on StripeCustomerSubscription models, which don't have that relation — throwing "labels is not defined on the model".

Impact

The bug's impact depends on the Stripe customer's existing subscriptions:

  • Customer with a paid subscription + comped: true: The comp conversion never runs. The member ends up with paid status instead of comped, and the API returns a 500. This is a real data issue — the member doesn't get their complimentary access.
  • Customer with an existing comp subscription: All database records are written correctly by linkStripeCustomer() before the error occurs, so the member is properly set up. However, the API still returns a 500 to the caller.
  • Customer with no subscriptions: The bug doesn't trigger because there are no StripeCustomerSubscription models to eager-load on.

Fix

Uses sharedOptions (only context + transacting) instead of the full options when calling setComplimentarySubscription() on line 406 of member-bread-service.js. This matches the pattern already established for the linkStripeCustomer() call on line 381.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bc6df2af-2889-4024-a595-484a0cd6b4db

📥 Commits

Reviewing files that changed from the base of the PR and between 62d8213 and da978ed.

⛔ Files ignored due to path filters (1)
  • ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • ghost/core/core/server/services/members/members-api/services/member-bread-service.js
  • ghost/core/test/e2e-api/admin/members.test.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • ghost/core/core/server/services/members/members-api/services/member-bread-service.js
  • ghost/core/test/e2e-api/admin/members.test.js

Walkthrough

The member-bread service's add method was changed so that when data.comped is truthy, the setComplimentarySubscription call receives sharedOptions (an object containing only transacting and context when present) instead of the full options. No other control flow, downstream calls, or return behavior in the add method were modified.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main bug fix: passing incorrect options to setComplimentarySubscription causing a 'labels is not defined' error when creating comped members via API.
Description check ✅ Passed The description clearly explains the bug, its impact, and the fix applied, directly relating to the changeset which modifies member-bread-service.js and adds a test.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ONC-1553_comped-api-err
📝 Coding Plan
  • Generate coding plan for human review comments

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.41.1)
ghost/core/test/e2e-api/admin/members.test.js

[]


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@troyciesco troyciesco marked this pull request as ready for review March 17, 2026 14:18
@troyciesco troyciesco requested a review from cmraible March 17, 2026 14:23
@troyciesco troyciesco force-pushed the ONC-1553_comped-api-err branch from 62d8213 to d0dd703 Compare March 18, 2026 13:02
Copy link
Collaborator

@cmraible cmraible left a comment

Choose a reason for hiding this comment

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

LGTM! I just pushed a single e2e-api test to cover this, mostly just to prevent incidental regressions in the future 🚢

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
ghost/core/test/e2e-api/admin/members.test.js (1)

1283-1354: LGTM! Well-structured test that directly verifies the bug fix.

The test correctly exercises the code path where a comped member is created with labels, which previously would fail with "labels is not defined on the model" error. The Stripe mocking pattern follows existing tests, and the assertions verify both the comped status and label presence.

One optional enhancement for consistency with other comped tests in this file:

💡 Optional: Add member event assertions for more comprehensive coverage
        assert.ok(member.labels.find(l => l.name === 'Complimentary'), 'Member should have Complimentary label');

+       // Verify member status events (optional - for parity with other comped tests)
+       await assertMemberEvents({
+           eventType: 'MemberStatusEvent',
+           memberId: member.id,
+           asserts: [{
+               from_status: null,
+               to_status: 'free'
+           }, {
+               from_status: 'free',
+               to_status: 'comped'
+           }]
+       });
    });

,

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/e2e-api/admin/members.test.js` around lines 1283 - 1354, Add
an optional assertion to verify member status events by calling
assertMemberEvents after the existing label assertions in the "Can create a
comped member with labels via API" test; specifically, invoke assertMemberEvents
with eventType 'MemberStatusEvent', the created member's id (member.id), and
asserts array checking transitions from null→'free' and 'free'→'comped' so the
test matches parity with other comped tests and ensures status-change events are
recorded.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ghost/core/test/e2e-api/admin/members.test.js`:
- Around line 1283-1354: Add an optional assertion to verify member status
events by calling assertMemberEvents after the existing label assertions in the
"Can create a comped member with labels via API" test; specifically, invoke
assertMemberEvents with eventType 'MemberStatusEvent', the created member's id
(member.id), and asserts array checking transitions from null→'free' and
'free'→'comped' so the test matches parity with other comped tests and ensures
status-change events are recorded.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f1d07c38-02cd-4315-a434-18963e1ed4ce

📥 Commits

Reviewing files that changed from the base of the PR and between d0dd703 and 2ebcec4.

⛔ Files ignored due to path filters (1)
  • ghost/core/test/e2e-api/admin/__snapshots__/members.test.js.snap is excluded by !**/*.snap
📒 Files selected for processing (1)
  • ghost/core/test/e2e-api/admin/members.test.js

@troyciesco troyciesco force-pushed the ONC-1553_comped-api-err branch from 2ebcec4 to da978ed Compare March 19, 2026 14:06
@troyciesco troyciesco merged commit f13ff58 into main Mar 19, 2026
34 checks passed
@troyciesco troyciesco deleted the ONC-1553_comped-api-err branch March 19, 2026 14:37
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