Guide
Errors
What to throw, what the client sees, and which code means what.
The convention
This library uses catchError rather than try/catch around every
await. It turns a rejected promise into a tuple, so the failure is a value
you handle rather than a jump.
import {
catchError, BeforeSave, validateOrThrow, CloudFunction,
} from 'parse-server-kit';
const [err, saved] = await catchError(product.save(null, { sessionToken }));
if (err) throw err;
return saved;
Use try/catch for synchronous work — JSON.parse, buffer
handling — and for a whole-function boundary that must never throw, such as a cron job.
Throwing something useful
throw new Parse.Error(142, 'Price must be greater than zero');
A Parse.Error reaches the client as {code, error} with a
sensible HTTP status. Anything else becomes a generic 141 with the message
attached, which is fine for genuine bugs and poor for expected refusals.
| Code | Name | Use for |
|---|---|---|
101 | OBJECT_NOT_FOUND | Missing — and what Parse returns when the ACL hides something |
102 | INVALID_QUERY | A malformed constraint |
119 | OPERATION_FORBIDDEN | Signed in, but not allowed |
141 | SCRIPT_FAILED | The default for an uncaught error |
142 | VALIDATION_ERROR | Input a caller can correct. validateOrThrow uses this |
202 / 203 | USERNAME_TAKEN / EMAIL_TAKEN | Signup collisions |
209 | INVALID_SESSION_TOKEN | Expired or revoked session |
5001 | CONFLICT | This library — somebody else changed it first |
useMasterKey: if it appears, it was permissions.
Validation errors
@BeforeSave()
static async onBeforeSave(req: Parse.Cloud.BeforeSaveRequest<Product>) {
validateOrThrow(req.object); // 142, listing every failure at once
}
validateOrThrow checks every rule declared with @ParseField and
reports them together, rather than one per round-trip. For a partial check without
throwing, validateObject returns {valid, errors[]}.
Write conflicts
try {
await job.save(null, { useMasterKey: true });
} catch (error) {
if (error instanceof Parse.Error && error.code === CONFLICT) {
// Reload, re-apply, retry - or surface CONFLICT_MESSAGE,
// which is already phrased for an end user.
}
throw error;
}
A lost optimistic lock and a lost transaction race report the same code, because to the person looking at the screen they are the same event.
What the client receives
removeResultMiddleware unwraps Parse's {result: …} envelope on success. Errors pass through unchanged:
// success
{ "objectId": "abc123", "name": "Widget" }
// failure
{ "code": 142, "error": "name is required" }
| Status | Comes from |
|---|---|
400 | Validation, and most Parse.Errors |
403 | restrictRoutes refusing a blocked path |
404 | Unknown route, or 101 |
405 | The HTTP method is not in methods |
429 | rateLimit exceeded, with Retry-After |
In practice
@CloudFunction({ methods: ['POST'], validation: { requireUser: true } })
static async publishArticle(req: Parse.Cloud.FunctionRequest) {
const user = req.user!;
const [findErr, article] = await catchError(
new Parse.Query(Article).get(req.params.id, {
sessionToken: user.getSessionToken(),
})
);
// 101 already means "missing, or not yours" - let it through.
if (findErr) throw findErr;
if (article!.get('status') === 'published') {
throw new Parse.Error(142, 'This article is already published');
}
if (!article!.get('body')) {
throw new Parse.Error(142, 'Add some content before publishing');
}
article!.set('status', 'published');
const [saveErr] = await catchError(
article!.save(null, { sessionToken: user.getSessionToken() })
);
if (saveErr) throw saveErr;
return article;
}
Three habits worth keeping: let permission errors stay ambiguous, use 142 for anything the caller can fix, and write messages a person could act on.
Logging
parse-server logs an uncaught error from a cloud function with the function name, the
parameters and the user. Deliberate Parse.Error throws are logged too, so a
noisy validation path is visible without extra instrumentation.
parse-server 8.5+ offers enableSanitizedErrorResponse, which strips detail
from responses while keeping it in the logs — worth turning on in production so an
internal failure does not describe itself to a caller.