balderdashy/sails · info

Warning: route `${address}` should explicitly declare `skipA

Error message

Warning: route `${address}` should explicitly declare `skipAssets: true` or `skipAssets: false` to ensure correct handling of assets!

What it means

For catch-all style routes (ending in `/*` or `/?`, or regex-syntax `r|...` routes) bound with permissive verbs (all/get/head/options) where the target omits `skipAssets`, Sails warns because static asset requests (images, js, css) may accidentally match the route. Explicitly declaring `skipAssets: true` or `false` makes intent clear.

Source

Thrown at lib/router/index.js:377

  // Updated the sorted route address cache
  sortedRouteAddresses = sortRouteAddresses(_.keys(this.explicitRoutes));

  // Iterate over each address and bind the route that the address is for.
  _.each(sortedRouteAddresses, function(address) {
    var target = self.explicitRoutes[address];
    var verb = detectVerb(address).verb;

    // If the route address ends in a pattern var (e.g. /:id) or a wildcard (i.e. /*)
    // and it declares a method that could be used to request an asset, and the route
    // doesn't explicitly declare `skipAssets` true or false, then it should!
    var shouldDeclareSkipAssets = (
      _.isUndefined(target.skipAssets) &&
      (address.match(/\/\*\/?$/) || address.match(/^r\|/)) &&
      (!verb || _.contains(['all', 'get', 'head', 'options'], verb))
    );
    if (shouldDeclareSkipAssets) {
      sails.log.warn('Warning: route `' + address + '` should explicitly declare `skipAssets: true` or `skipAssets: false` to ensure correct handling of assets!');
      sails.log.warn('See http://sailsjs.com/docs/concepts/routes/url-slugs for more info.');
      console.log();
    }

    self.bind(address, target);
  });

  // Fired after static routes are bound
  sails.emit('router:after');
};


/**
 * Given a route target configuration, return an action identity for that target.
 * @param  {Dictionary|String} target The route target to get an action identity for
 * @return {String}        An action identity like `user/find`
 */
Router.prototype.getActionIdentityForTarget = function getActionIdentityForTarget(target) {

View on GitHub (pinned to 7b76422cc2)

Solutions

  1. Add `skipAssets: true` to the route target so it ignores static asset URLs
  2. Add `skipAssets: false` explicitly if you truly want the route to handle asset paths
  3. Scope the catch-all pattern more narrowly (e.g. `/page/*`) to avoid the ambiguity

Example fix

// before
'GET /*': 'PageController.show'
// after
'GET /*': { view: 'homepage', skipAssets: true }
Defensive patterns

Strategy: validation

Validate before calling

function assertSkipAssets(routes) {
  for (const [addr, target] of Object.entries(routes)) {
    if (/\*\/?$/.test(addr) && target && target.skipAssets === undefined) {
      console.warn(`Route ${addr} should declare skipAssets: true|false`);
    }
  }
}

Prevention

When it happens

Trigger: Binding e.g. `'GET /*': 'PageController.show'` or any `/*`-style route without a `skipAssets` property in the target object.

Common situations: Catch-all routing for SPAs or CMS-style pages; developers forget that without skipAssets the route will also swallow requests for static files.

Related errors


AI-assisted analysis of balderdashy/sails@7b76422cc2 (2026-09-01). Data as JSON: /api/errors/044980fe7888e96b. Report an issue: GitHub.