Is this a bug? filter:primary_tag seems to filtering primary_tag.slug instead

I’m working on a dev copy of a theme being used on a site hosted by Ghost.
I’m on a Mac, the installed Ghost version is 6.21.0 and Node is 22.21.1.
The hosted site is on Ghost 6.21.0-0-gd8b5e756+moya.

This theme retrieves values from a custom option

tag = @custom.first_section_tag_or_title

then uses that variable in a filter

{{#get “posts” … filter=“primary_tag:{{tag}}”}}

but unlike on the live site, on my dev site that does not return the matching posts. I have confirmed the variable is being set correctly and passed to the template where it will be used.

One possible value for {{tag}} is “2-Tier” but when I hard code it as

filter=“primary_tag:2-Tier”

the matching posts still are not returned. However when I change the case and hard code it as

filter=“primary_tag:2-tier”

it does return the expected posts.

It seems to me that for some reason, unlike the live instance, my dev installation of Ghost is filtering on primary_tag.slug when it should be filtering on primary_tag, and further it is not correcting it to lowercase as it would if I was intentionally filtering on the slug version.

The theme is ridiculously complicated with lots of (read “way too many”) custom options and I’m not entirely sure which ones are in use on the site, so I’m leery of changing things to work on my end only to discover I’ve broken something when it goes live.

Does anyone have any ideas what could be causing this other than a bug?

That’s the expected behavior. Filtering on tag: or primary_tag actually matches the slug. (The tag is an object.) So… given the complexity of your theme, I suspect something about your setup is slightly different. Or you’ve found a discrepancy between sqlite (which you’re presumably running locally) vs mysql (run on Ghost Pro) – there are definitely a few of those!

Aside: Quirkily, the #has helper does actually take the tag.name, not tag.slug. :person_shrugging:

Thanks. It seems weird that if it’s matching on the slug it’s not normalizing it to lowercase first as the documentation suggests it should. Changing the value to lowercase in the theme options is getting me the posts but I need to make sure that isn’t going to affect anything else before I have them change it in production.

Slugs should always be lowercase.

Right. But what I’m saying is the variable being passed isn’t starting life as a slug and primary_tag:{{tag}} isn’t lowercasing it to match a slug. At any rate I’ve got my workaround figured out, but it’s not what the documentation says should be happening.

Does the documentation say somewhere that the filter expression will automatically lower-case for you? Sounds like the docs might need fixing, if so! :)

I haven’t looked to confirm so it might just have been my inference that if “tag:” actually matches “slug” and “slug” is always lowercase and this theme works in production when being passed an uppercased variable to match against, that normalization to lowercase must be happening. I guess normalization could be in the MySQL configuration but that seems odd and overhead-y to me.

Anyway, I’ve got what I need in dev and in addition to making mods to this theme we’re starting to make plans for a custom theme from the ground up that will absolutely not have this overly-complex custom values to set layout options silliness baked into it.

From “Cathy Sarisky (Spectral Web Services)” <notifications@ghost2.discoursemail.com>
To brook@brookellingwood.com
Date 3/9/2026 5:11:56 PM
Subject [Ghost Forum] [Developer help] Is this a bug? filter:primary_tag seems to filtering primary_tag.slug instead

In theory, knex makes sqlite3 and MySQL behave exactly the same. In practice, well… :woman_shrugging: I’ve definitely got examples where that’s not true. I’m moving at least some of my theme dev work into docker containers with MySQL in part to deal with that.