{"record":{"id":"da6805eea3e8492e","repo":"balderdashy/sails","slug":"files-in-the-controllers-directory-may-be-tradit","errorCode":null,"errorMessage":"Files in the `controllers` directory may be traditional controllers or \naction files.  Traditional controllers are dictionaries of actions, with \npascal-cased filenames ending in \"Controller\" (e.g. MyGreatController.js).\nAction files are kebab-cased (e.g. do-stuff.js) and contain a single action.\nThe following file${garbage.length > 1 ? 's were' : ' was'} ignored for not meeting those criteria:","messagePattern":"Files in the `controllers` directory may be traditional controllers or \naction files\\.  Traditional controllers are dictionaries of actions, with \npascal-cased filenames ending in \"Controller\" \\(e\\.g\\. MyGreatController\\.js\\)\\.\nAction files are kebab-cased \\(e\\.g\\. do-stuff\\.js\\) and contain a single action\\.\nThe following file(.+?) ignored for not meeting those criteria:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/app/private/controller/load-action-modules.js","lineNumber":180,"sourceCode":"          // Flag that an action with the given identity was successfully loaded from disk.\n          actionsLoadedFromDisk[actionIdentity] = true;\n\n        } // </ is it an action?>\n\n        // Otherwise give up on this file, it's GARBAGE.\n        // No, no, it's probably a very nice file but it's\n        // no controller as far as we're concerned.\n        else {\n          garbage.push(filePath);\n        } // </ it is garbage>\n\n      }); // </each(file from includeAll)>\n\n\n      // Complain about garbage.\n      if (garbage.length) {\n        sails.log.warn('---------------------------------------------------------------------------');\n        sails.log.warn('Files in the `controllers` directory may be traditional controllers or \\n' +\n                     'action files.  Traditional controllers are dictionaries of actions, with \\n' +\n                     'pascal-cased filenames ending in \"Controller\" (e.g. MyGreatController.js).\\n' +\n                     'Action files are kebab-cased (e.g. do-stuff.js) and contain a single action.\\n'+\n                     'The following file'+(garbage.length > 1 ? 's were' : ' was')+' ignored for not meeting those criteria:');\n        _.each(garbage, function(filePath){sails.log.warn('- '+filePath);});\n        sails.log.warn('----------------------------------------------------------------------------\\n');\n      }\n\n      // (Shallow) merge stuff from sails.config.controllers.moduleDefinitions on top of any loaded files.\n      // Note that the third argument (force) to `helpRegisterAction` is `true`, so there's no danger\n      // of identity conflicts.  Actions defined in `moduleDefinitions` will override anything else.\n      _.each(_.get(sails, 'config.controllers.moduleDefinitions') || {}, function(action, actionIdentity) {\n        helpRegisterAction(sails, action, actionIdentity, true);\n      });\n\n    } catch (e) { return cb(e); }\n\n    // Get a list of the action identities.","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/balderdashy/sails/blob/7b76422cc27823df033572bdda5c4910a68b697f/lib/app/private/controller/load-action-modules.js#L162-L198","documentation":"When loading action modules from api/controllers, Sails classifies each file as either a traditional controller (pascal-cased name ending in 'Controller', exporting a dictionary of actions) or an action file (kebab-cased, exporting a single action). Files matching neither convention are 'garbage': they are ignored, and this warning lists the naming rules and the offending files.","triggerScenarios":"api/controllers contains files whose names are neither PascalCase ending in Controller.js nor kebab-case .js action files — e.g. camelCase.js, snake_case.js, 'My Controller.js', or non-.js files picked up by includeAll.","commonSituations":"Utility/helper files stashed in api/controllers, files named with spaces or underscores, leftover backups like oldController.js.bak, or generated code that used the wrong casing convention.","solutions":["Rename the listed files to kebab-case (do-stuff.js) if they are single actions.","Rename to pascal-case ending in Controller (UserController.js) if they are traditional controllers.","Move non-action utility files out of api/controllers into api/services or a lib/ folder."],"exampleFix":"// before\napi/controllers/user_helpers.js      // ignored, warned\n// after\napi/controllers/user-helpers.js      // valid action file (or move to api/services/)","handlingStrategy":"validation","validationCode":"const files = fs.readdirSync('api/controllers');\nconst bad = files.filter(f => !/^[A-Z][A-Za-z0-9]*Controller\\.js$/.test(f) && !/^[a-z0-9]+(-[a-z0-9]+)*\\.js$/.test(f));\nif (bad.length) console.warn('Non-conforming controller files:', bad);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Follow kebab-case for action files and PascalCase+Controller for controller files.","Keep utilities out of api/controllers.","Run a lint/filename check in CI before lifting."],"tags":["controllers","file-naming","convention","ignored-files"],"backgroundTag":"invalid-filename-convention","analyzedSha":"7b76422cc27823df033572bdda5c4910a68b697f","analyzedAt":"2026-09-01T04:01:57.104Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}