Repository navigation
Description
New Issue Checklist
- I am not disclosing a vulnerability.
- I am not just asking a question.
- I have searched through existing issues.
- I can reproduce the issue with the latest versions of Parse Server and the Parse JS SDK.
Issue Description
REQUEST_ATTEMPT_LIMIT has no effect when fetch throws, which covers the most common transient failures in a Node service: connection reset, socket closed by the peer, connect timeout, DNS failure. Those requests are rejected after a single attempt.
The retry branch is only reachable when fetch returned a response:
https://github.com/parse-community/Parse-SDK-JS/blob/alpha/src/RESTController.ts#L219-L233
} else if (status >= 500 || status === 0) {
// retry on 5XX or library error
...
if (!isStream && ++attempts < CoreManager.get('REQUEST_ATTEMPT_LIMIT')) {When fetch throws, control goes to the catch instead, which rejects with no retry:
https://github.com/parse-community/Parse-SDK-JS/blob/alpha/src/RESTController.ts#L237-L245
Two things makes me think this is a regression from the XHR to fetch migration:
1. status === 0 is unreachable under fetch. A Response always carries a status in 200–599; status 0 only exists for opaque responses, which Node never produces. Under XMLHttpRequest, status === 0 used to signal a network error, which is probably why the retry was expected to cover. The comment on L220, "retry on 5XX or library error", still describes that behaviour.
2. The existing tests simulate that impossible condition with a mock, so they pass while the path that runs is broken. In src/__tests__/RESTController-test.js:
it('retries on connection failure', async () => {
mockFetch([{ status: 0 }, { status: 0 }, { status: 0 }, { status: 0 }, { status: 0 }])
await expect(RESTController.ajax('POST', 'users', {})).rejects.toEqual(
'Unable to connect to the Parse API'
);
expect(fetch.mock.calls.length).toBe(5);
});mockFetch resolves a response with status: 0. Real fetch rejects instead, so this test exercises only the dead branch. returns a connection error on network failure has the same shape, and asserts the XMLHttpRequest failed: "Unable to connect to the Parse API" message that only that branch can produce.
Also, ECONNREFUSED is rejected as a bare string on L241. handleError then finds no .code, .error or .message to read, falls through to its second branch, and produces ParseError(100, 'XMLHttpRequest failed: "Unable to connect to the Parse API"') — so the underlying ECONNREFUSED is lost and the caller gets XHR wording.
Steps to reproduce
A local HTTP server accepts the connection and destroys the socket, which makes fetch throw TypeError: fetch failed.
const http = require('http')
const Parse = require('parse/node')
const run = async () => {
let attempts = 0
const server = http.createServer((req) => {
attempts++
req.socket.destroy()
})
await new Promise((r) => server.listen(0, '127.0.0.1', r))
Parse.initialize('appId', null, 'masterKey')
Parse.serverURL = `http://127.0.0.1:${server.address().port}/parse`
Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 5)
try {
await new Parse.Query('Foo').first({ useMasterKey: true })
} catch (e) {
console.log('REQUEST_ATTEMPT_LIMIT :', Parse.CoreManager.get('REQUEST_ATTEMPT_LIMIT'))
console.log('actual HTTP attempts :', attempts)
}
server.close()
process.exit(0)
}
run()Actual Outcome
REQUEST_ATTEMPT_LIMIT : 5
actual HTTP attempts : 1
Expected Outcome
A thrown network error should be eligible for the same retry policy as a 5xx response, up to REQUEST_ATTEMPT_LIMIT, since by definition no response was received:
REQUEST_ATTEMPT_LIMIT : 5
actual HTTP attempts : 5
Caveat: retrying a non-idempotent request after a throw is not unconditionally safe, because the request may have been received but unanswered. We handle that by gating only on tunneled GET requests and it recovers effectively.
Environment
Server
- Parse Server version:
9.9.0 - Operating system:
Ubuntu 26.04 (Heroku-26 stack), Node.js 24.16.0 - Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc):
Heroku
Database
- System (MongoDB or Postgres):
MongoDB - Database version:
n/a — database involved - Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc):
MongoDB Atlas
Client
- Parse JS SDK version:
8.6.2
Activity
parse-github-assistant commentedon Oct 15, 2026
🚀 Thanks for opening this issue!
ℹ️ You can help us to fix this issue faster by opening a pull request with a failing test. See our Contribution Guide for how to make a pull request, or read our New Contributor's Guide if this is your first time contributing.