Error from ghost log:
[2025-01-21 15:27:49] ERROR "POST /blog/ghost/api/admin/settings/routes/yaml/" 500 7074ms
NAME: InternalServerError
MESSAGE: Could not load routes.yaml file.
level: critical
InternalServerError: Could not load routes.yaml file.
at /var/www/html/ghost/versions/5.105.0/core/server/services/route-settings/RouteSettings.js:129:35
at async Object.query (/var/www/html/ghost/versions/5.105.0/core/server/api/endpoints/settings.js:158:13)
at async getResponse (/var/www/html/ghost/versions/5.105.0/node_modules/@tryghost/api-framework/lib/pipeline.js:259:34)
at async ImplWrapper (/var/www/html/ghost/versions/5.105.0/node_modules/@tryghost/api-framework/lib/pipeline.js:264:30)
at async Http (/var/www/html/ghost/versions/5.105.0/node_modules/@tryghost/api-framework/lib/http.js:70:28)
I have tried 3-4 variants of this routes.yaml, it always breaks something, am I doing this the right way?
TypeError: begin.toISOString is not a function
at #fetchEvents (/var/www/html/ghost/versions/5.105.0/node_modules/@tryghost/email-analytics-service/lib/EmailAnalyticsService.js:293:64)
at EmailAnalyticsService.fetchMissing (/var/www/html/ghost/versions/5.105.0/node_modules/@tryghost/email-analytics-service/lib/EmailAnalyticsService.js:185:39)
at async EmailAnalyticsServiceWrapper.fetchMissing (/var/www/html/ghost/versions/5.105.0/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js:107:29)
at async EmailAnalyticsServiceWrapper.startFetch (/var/www/html/ghost/versions/5.105.0/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js:148:24)
at async /var/www/html/ghost/versions/5.105.0/core/server/services/email-analytics/EmailAnalyticsServiceWrapper.js:63:13
at async EventEmitter. (/var/www/html/ghost/versions/5.105.0/node_modules/@tryghost/domain-events/lib/DomainEvents.js:34:17)
Welcome to the club, routes are so much fun. I use AI support for routes, try Claude Sonnet or Gemini Flash 2, here’s Gemini thought on your routes …
The Problem: Conflicting Filters and the Root Collection
The Root Collection (/) and Universality: The root collection (/) is generally designed to be a catch-all for posts not belonging to any specific collection. It’s intended to handle the default routing for your blog. Applying a restrictive filter to the root collection is often problematic.
Conflicting Exclusion Filters: The filter: tag:-fr+tag:-tree-species attempts to exclude both posts tagged with -fr AND posts tagged with -tree-species. This means only posts that have neither of these tags will be included in this collection. This is almost certainly not what you want. Usually, + in a filter means “AND”, but here you’re effectively creating a highly exclusive filter at your site’s root.
Conflict with /blog/fr/: The second collection /blog/fr/ has a filter: tag:-fr. If a post has the fr tag, it will be excluded from the root collection (due to tag:-fr) and excluded from the /blog/fr/ collection as well. This means posts with the fr tag will not be displayed anywhere.
Permalink Collision: The permalink: /{slug}/ in the root collection makes every post’s URL /slug-name/. The /blog/fr/ collection attempts to create URLs like /blog/fr/slug-name/, but because every slug is already used by the root collection, Ghost will have routing conflicts.
I have been trying AI for this as well, but it seems to struggle with ghosts routing. I have tried probably 20 different variations and no matter what I break one thing or another. Ghost really does not me hiding stuff it seems, kinda funny considering the name.
collections:
/:
permalink: /{slug}/
filter: tags:-[fr,tree-species]
template: index
/blog/fr/:
permalink: /blog/fr/{slug}/
filter: tag:fr
template: index
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/
See if the tags:[ ] notation works better for you. Also, I’m assuming you want the posts tagged ‘fr’ on the /blog/fr collection, so I removed a stray - for you.
You still need a route where posts tagged tree species can live.
One other thought - I’m not 100% sure you can put a collection at /blog/fr. I’m sure the permalinks are fine, but it’s possible that the collection needs to sit at /fr/ or /blog-fr/ instead. If it’s still throwing that error, you might try making that change and see if it fixes it.
Routes don’t get updated if a post is tagged with the bulk edit from the list of posts. Could that have happened here? Try either restarting ghost or loading a new copy of the routes file to trigger.
It wasn’t bulk edit, it was from inside the specific posts editor. I have done as you suggested and restarted ghost, I also reuploaded the routes file to be sure.
Interesting! I see from https://boisafeudunord.com/blog/sitemap-posts.xml that you’ve got a subdirectory install. I wonder if there’s some ah… quirky… behavior for subdirectory installs and routing.
Wait.. Hang on. Your trees posts are actually at blog/trees. Etc. So you need to change the routing for the middle section to
/fr/:
permalink: /fr/{slug}/
filter: tag:fr
template: index
Which, because of the subdirectory install will result in things at /blog/fr, but… yeah. Try that please.