balderdashy / balderdashy/sails

Throwing exit signals within helpers and actions vs. invoking a callback (`throw 'foo';` vs `return exits.foo();`, and .intercept()

Open
#4,339 6 comments 0 reactions 0 assignees View on GitHub
helpful info or workaround question
Dominant language
JavaScript
Stars
22.8k
Forks
1.9k
PR merge metrics
No merged PRs in 30d

Description

**Sails version**: 1.0.0-46
**Node version**: 9.5.0
**NPM version**: 5.6.0
**DB adapter name**: sails-mysql
**DB adapter version**: 1.0.0-17
**Operating system**: macOS High Sierra 10.13.1


Creating a helper that awaits findOne then checking the result is not undefined for some reason causes the controller that called it to produce the error:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
WARNING: Something seems to be wrong with this function.
It is trying to signal that it has finished AGAIN, after
already resolving/rejecting once.
(silently ignoring this...)

To assist you in hunting this down, here is a stack trace:
```
at Object._requestHandler [as get-by-id] (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/machine-as-action/lib/machine-as-action.js:444:16)
at /Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/sails/lib/router/bind.js:247:46
at routeTargetFnWrapper (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/sails/lib/router/bind.js:391:9)
at /Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/sails/lib/router/bind.js:454:14
at Layer.handle [as handle_request] (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/layer.js:95:5)
at /Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:281:22
at param (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:354:14)
at param (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:365:14)
at Function.process_params (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:410:3)
at next (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:275:10)
at next (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/route.js:127:14)
at Hook.expressMiddleware (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/sails/lib/hooks/i18n/index.js:202:14)
at Hook.wrapper [as expressMiddleware] (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/@sailshq/lodash/lib/index.js:3250:19)
at addLocalizationMethod (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/sails/lib/hooks/i18n/index.js:147:35)
at routeTargetFnWrapper (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/sails/lib/router/bind.js:391:9)
at Layer.handle [as handle_request] (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/layer.js:95:5)
at /Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:281:22
at param (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:354:14)
at param (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:365:14)
at Function.process_params (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:410:3)
at next (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/index.js:275:10)
at next (/Users/benbrookes/Desktop/Dev/reproductions/sails-tests/node_modules/express/lib/router/route.js:127:14)
```

[?] For more help, visit https://sailsjs.com/support
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

This has been reproduced in a fresh sails project.

To reproduce this, create a new sails project and select empty project.

`models/TestModel.js`
```
module.exports = {
attributes: {
name: {
type: "string",
}
}
}
```

`helpers/get-by-id.js`
```
module.exports = {
friendlyName: "Get Record By Id",
description: "Retrieves a single record by Id",
inputs: {
id: {
type: "number",
required: true
}
},
exits: {
recordNotFound: {
description: "The specified record was not found"
}
},
fn: async function(inputs, exits){
await TestModel.findOne({
id: inputs.id
}).catch((err) => {
return exits.error();
}).then((record) => {
if(!record) {
return exits.recordNotFound();
}
return exits.success(record);
});
}
}
```

`controllers/get-by-id.js`
```
module.exports = {
friendlyName: "Get By Id",
inputs: {
id: {
type: "number",
required: true
}
},
exits: {
notFound: {
statusCode: 404,
responseType: ""
},
success: {
statusCode: 200,
responseType: ""
},
serverError: {
statusCode: 500,
responseType: ""
}
},
fn: async function(inputs, exits){
let result = await sails.helpers.getById(inputs.id)
.tolerate("recordNotFound", () => {
return exits.notFound();
}).intercept(() => {
return exits.serverError();
})

return exits.success(result);
}
}
```

API request endpoint: `GET /api/:id`

Original title: Model.findOne causes exit signal error in controller when used in a helper

Contributor guide

Open the contributing guide

Research direction

Start with the reproduced flow in helpers/get-by-id.js and controllers/get-by-id.js, then inspect how the helper's async fn interacts with .tolerate() and .intercept(). Reproduce GET /api/:id and trace the duplicate exit warning; done means the request reaches the intended exit without signaling completion twice.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.