TryGhost/Ghost · error · BadRequestError
The newsletter parameter doesn't match any active newsletter
Error message
The newsletter parameter doesn't match any active newsletter.
What it means
A BadRequestError thrown when publishing or scheduling a post and the supplied `newsletter` query parameter (a slug) does not resolve to an active newsletter via `Newsletter.findOne({slug}, {filter: 'status:active'})`. The check only runs when the post is transitioning to published/scheduled/sent without an existing `newsletter_id`. The newsletter must be active — archived newsletters are filtered out.
Source
Thrown at ghost/core/core/server/models/post.js:797
this.set('published_by', String(userId));
}
} else {
// In any other case (except import), `published_by` should not be changed
if (this.hasChanged('published_by') && !options.importing) {
this.set('published_by', this.previous('published_by') ? String(this.previous('published_by')) : null);
}
}
// newsletter_id is read-only and should only be set using the newsletter param when publishing/scheduling
if (options.newsletter
&& !this.get('newsletter_id')
&& this.hasChanged('status')
&& (newStatus === 'published' || newStatus === 'scheduled' || newStatus === 'sent')) {
// Map the passed slug to the id + validate the passed newsletter
ops.push(async () => {
const newsletter = await Newsletter.findOne({slug: options.newsletter}, {transacting: options.transacting, filter: 'status:active'});
if (!newsletter) {
throw new BadRequestError({
message: messages.invalidNewsletter
});
}
this.set('newsletter_id', newsletter.id);
});
// If the `email_segment` isn't passed at the same time, reset it to be 100% sure that they can only be used together
this.set('email_recipient_filter', 'all');
// email_segment is read-only and should only be set using a query param when publishing/scheduling
// we can't set it if we don't pass newsletter
if (options.email_segment) {
this.set('email_recipient_filter', options.email_segment);
}
}
// ensure draft posts have the email_recipient_filter reset unless an email has already been sent
if (newStatus === 'draft' && this.hasChanged('status')) {View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Fetch active newsletters first (`/newsletters/?filter=status:active`) and pass a verified `slug` from that list.
- Confirm the newsletter is in `active` status, not archived.
- Pass the newsletter `slug` (not id or display name) as the `newsletter` param.
- If no active newsletter exists, create/reactivate one before publishing with email.
Example fix
// before
await api.posts.edit({id, status: 'published'}, {newsletter: 'Weekly-Digest'}); // wrong case / archived
// after
const active = await api.newsletters.browse({filter: 'status:active'});
const slug = active.newsletters[0].slug;
await api.posts.edit({id, status: 'published'}, {newsletter: slug}); Defensive patterns
Strategy: validation
Validate before calling
async function resolveActiveNewsletterSlug(api, requestedSlug) {
const res = await api.newsletters.browse({filter: 'status:active'});
const slugs = res.newsletters.map(n => n.slug);
if (!slugs.includes(requestedSlug)) throw new Error(`Newsletter '${requestedSlug}' is not active; available: ${slugs.join(', ')}`);
return requestedSlug;
} Type guard
const isActiveNewsletterRef = (n) => n && typeof n.slug === 'string' && n.status === 'active';
Try / catch
try {
await api.posts.edit({id, status: 'published'}, {newsletter: slug});
} catch (err) {
if (err.type === 'BadRequestError' && /doesn't match any active newsletter/i.test(err.message)) refreshNewsletters();
else throw err;
} Prevention
- Always pass a slug sourced from a fresh active-newsletter list.
- Re-fetch newsletters before publish if the page has been open a while (newsletters may be archived).
- Pass the slug, never the id or display name.
When it happens
Trigger: PUT/PATCH a post to `published`/`scheduled` with `?newsletter=foo` where `foo` is a nonexistent slug, a typo, or the slug of an archived/inactive newsletter. Also when the `status:active` filter excludes the only matching newsletter.
Common situations: The newsletter was archived between page load and publish; the client sends the newsletter's UUID/name instead of its slug; a typo in the slug; the newsletter feature is disabled and no active newsletters exist; the slug's case differs from the stored value.
Related errors
- post.email.error
- Subject is required
- Email content is required
- Lexical must be a valid JSON string
- Token is required
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/19fe3c2928cc4994.
Report an issue: GitHub.