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

  1. Fetch active newsletters first (`/newsletters/?filter=status:active`) and pass a verified `slug` from that list.
  2. Confirm the newsletter is in `active` status, not archived.
  3. Pass the newsletter `slug` (not id or display name) as the `newsletter` param.
  4. 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

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


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/19fe3c2928cc4994. Report an issue: GitHub.