Routes.yml collections trailing slash error

Ghost Details:
Version: 5.107.0
Environment: production
Database: mysql8
Mail: Direct


Ghost is relatively up to date, running behind an nginx reverse proxy on arch linux container with the docker-compose settings:

version: '3.3'

services:
  blog:
    image: ghost:5
    restart: always
    ports:
      - 8384:2368
    volumes:
      - ~/ghost/content:/var/lib/ghost/content
    environment:
      # see https://ghost.org/docs/config/#configuration-options
      database__client: mysql
      database__connection__host: database
      database__connection__user: xxx
      database__connection__password: xxx
      database__connection__database: xxx
      url: https://xxx.com/
      NODE_ENV: production

  database:
    image: mysql:8.4.0-oraclelinux8
    restart: always
    volumes:
      - ~/data/db:/var/lib/mysql
    ports:
      - "13928:3306"
    environment:
      MYSQL_ROOT_PASSWORD: xxx
      MYSQL_DATABASE: xxx
      MYSQL_USER: xxx
      MYSQL_PASSWORD: xxx

my current working routes.yaml just has a minor change to the order of the posts.

routes:

collections:
  /:
    permalink: /{slug}/
    template: index
    order: published_at asc


taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/

I want to set it up into 2+ collections. It is effectively a travel blog, where I want a collection for each trip i take as I am about to embark on a second trip I want to get this configured. I have been through the docs, this forum, and the blog post where it suggests something to me which would look like this

routes:
  collections:
    /france-2025/:
      permalink: /france-2025/{slug}/
      template: index
      filter: tag:France-2025

    /india-2024/:
      permalink: /india-2024/{slug}/
      template: index
      filter: tag:India-2024
      order: published_at asc

    /:
      permalink: /{slug}/
      template: index
      order: published_at asc

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/%

I have been up hill and down dale trying to get this to work but always come back to trailing slash errors on collections.

{
  "name": "Log",
  "hostname": "c7ef969f7fb2",
  "pid": 1,
  "level": 50,
  "version": "5.107.0",
  "err": {
    "id": "35174aa0-d7eb-11ef-8dfe-2b9b43bec286",
    "domain": "https://tomsgone.com/",
    "code": null,
    "name": "ValidationError",
    "statusCode": 422,
    "level": "normal",
    "message": "The following definition \"collections\" is invalid: A trailing slash is required.",
    "stack": "ValidationError: The following definition \"collections\" is invalid: A trailing slash is required.\n    at /var/lib/ghost/versions/5.107.0/core/server/services/route-settings/validate.js:206:19\n    at /var/lib/ghost/versions/5.107.0/node_modules/lodash/lodash.js:4967:15\n    at baseForOwn (/var/lib/ghost/versions/5.107.0/node_modules/lodash/lodash.js:3032:24)\n    at /var/lib/ghost/versions/5.107.0/node_modules/lodash/lodash.js:4936:18\n    at Function.forEach (/var/lib/ghost/versions/5.107.0/node_modules/lodash/lodash.js:9410:14)\n    at Object.validateRoutes (/var/lib/ghost/versions/5.107.0/core/server/services/route-settings/validate.js:203:7)\n    at validate (/var/lib/ghost/versions/5.107.0/core/server/services/route-settings/validate.js:434:30)\n    at SettingsLoader.loadSettings (/var/lib/ghost/versions/5.107.0/core/server/services/route-settings/SettingsLoader.js:36:20)\n    at async initDynamicRouting (/var/lib/ghost/versions/5.107.0/core/boot.js:283:27)\n    at async bootGhost (/var/lib/ghost/versions/5.107.0/core/boot.js:582:13)",
    "hideStack": false
  },
  "msg": "The following definition \"collections\" is invalid: A trailing slash is required.",
  "time": "2025-01-21T11:31:07.980Z",
  "v": 0
}

and

[2025-01-21 11:07:17] ERROR "POST /ghost/api/admin/settings/routes/yaml/" 422 88ms

The following definition "collections" is invalid: A trailing slash is required.

Error ID:
    e0b0b8f0-d7e7-11ef-b782-672c7e8b3892

----------------------------------------

ValidationError: The following definition "collections" is invalid: A trailing slash is required.
    at /var/lib/ghost/versions/5.105.0/core/server/services/route-settings/validate.js:206:19
    at /var/lib/ghost/versions/5.105.0/node_modules/lodash/lodash.js:4967:15
    at baseForOwn (/var/lib/ghost/versions/5.105.0/node_modules/lodash/lodash.js:3032:24)
    at /var/lib/ghost/versions/5.105.0/node_modules/lodash/lodash.js:4936:18
    at Function.forEach (/var/lib/ghost/versions/5.105.0/node_modules/lodash/lodash.js:9410:14)
    at Object.validateRoutes (/var/lib/ghost/versions/5.105.0/core/server/services/route-settings/validate.js:203:7)
    at validate (/var/lib/ghost/versions/5.105.0/core/server/services/route-settings/validate.js:434:30)
    at SettingsLoader.loadSettings (/var/lib/ghost/versions/5.105.0/core/server/services/route-settings/SettingsLoader.js:36:20)
    at async Bridge.reloadFrontend (/var/lib/ghost/versions/5.105.0/core/bridge.js:99:28)
    at async RouteSettings.setFromFilePath (/var/lib/ghost/versions/5.105.0/core/server/services/route-settings/RouteSettings.js:109:13)
    at async Object.query (/var/lib/ghost/versions/5.105.0/core/server/api/endpoints/settings.js:158:13)
    at async getResponse (/var/lib/ghost/versions/5.105.0/node_modules/@tryghost/api-framework/lib/pipeline.js:259:34)
    at async ImplWrapper (/var/lib/ghost/versions/5.105.0/node_modules/@tryghost/api-framework/lib/pipeline.js:264:30)
    at async Http (/var/lib/ghost/versions/5.105.0/node_modules/@tryghost/api-framework/lib/http.js:70:28)

I have updated ghost and restarted everything and even tried enlisting chatgpt but I can’t get past it. I checked the yaml with a linter and tried reordering the stanzas in the yaml, I can’t make out anywhere there should be a slash.

I would appreciate anyone pointing me in the right direction for what I am trying to achieve.

Thank you

It’s that percent at the end of your author taxonomy, I think.

Also, tags should be slug form for the filter, so change to lowercase for most predictable results.

Whoa, hang on. Nope, your problem is you have collections nested under routes. Routes is a separate section (May be empty) and collections should start with no spaces before it - you’ve got it indebted.

Thanks! It was a bit of both your posts to get it working in the end. I had been staring at it for too long.

I include the working routes.yaml here in case it is useful in the future for anyone else.

routes:

collections:

  /india-2024/:
    permalink: /india-2024/{slug}/
    template: index
    filter: tag:india-2024
    order: published_at asc

  /france-2025/:
    permalink: /france-2025/{slug}/
    template: index
    filter: tag:france-2025
    
  /:
    permalink: /{slug}/
    template: index
    order: published_at asc

taxonomies:
  tag: /tag/{slug}/
  author: /author/{slug}/
2 Likes