Upgrading from 2.56 #
This release is not breaking. It is the first Kuzzle built entirely from TypeScript in strict mode, and the migration was held to a no behaviour change rule: the functional test suites of v2.56.0 pass unchanged against it.
This page lists what you can observe after the upgrade (fixes of long-standing defects, and a few additions), so that you can check it against your usage. Items that may need an action on your side are marked Action.
The changelog is generated from the commits and says what was done. This page says what you may have to check.
Operators #
Configuration and HTTP #
- Multipart uploads are now size-limited.
limits.maxFormFileSize(default 1 MB) was never enforced; now it is. A multipart file above it gets413 network.http.file_too_large, even wheremaxRequestSizewas raised. Action: if you accept larger uploads, raiselimits.maxFormFileSizetoo. - HTTP connections carry their real headers. They were always empty (
{}). They now reach theconnection:new/connection:removehook payloads, and thecombinedaccess-log format fills its referer and user-agent fields (they were always-). Thelogstashaccess-log format keeps the v2.56.0 shape: the request headers —authorizationandcookieincluded — are logged once, underextra.headers, as in v2.56.0, and theconnectionobjects of an entry carry noheadersfield (v2.56.0 logged an empty{}there). Action: if yourconnection:*hooks forward their payload somewhereAuthorizationor cookie values must not go, filter them there. - Node identity. Each node now has a name (
knode-…unless yourBackendnames it). It appears in the Redis client name (CLIENT LIST), the cluster ID card, thenodefield of realtime notifications and of every API response, and theX-Kuzzle-NodeHTTP header (which read the literal string"undefined").
Cluster #
- A node that misses a sync message now asks its sender to retransmit it instead of leaving the cluster. The new setting
cluster.retransmitBuffercontrols it ({ messages: 1000, bytes: 16 MiB }by default;0in either field disables it). A rolling upgrade from v2.56.0 is supported: an older node answers the new request with "unknown", and the newer one falls back to the old behaviour. Until every node runs this version, a lost message is therefore handled as on v2.56.0. - An evicted node exits with code 1. It used to stay up, detached from the cluster, with state that had stopped advancing. With a restart policy, it comes back.
- Joining a cluster is more reliable. An existing node now answers a joiner's handshake within its timeout; about 1–3 % of joins failed at the default heartbeat, more with a longer one.
- A node that stops, including one evicted while it is still starting, frees the cluster-wide locks it holds before it exits. The other nodes no longer wait for those locks to expire.
Command line and runtime #
start-kuzzle-serveroptions work. They were all ignored (the argument parser was given an empty list). Now--mappings,--fixturesand--securitiesimport at startup,--vault-key/--secrets-fileoverride the environment variables,--help/--versionprint and exit, and an option given without a value stops the boot with a message.--enable-pluginsnames a plugin the published package does not ship: it logs a warning and the boot continues. Action: if your deployment passes options to this command (the Docker image's default command does not), check that they still do what you meant.- Dumps (
dump.enabled, off by default). A dump's suffix is validated:admin:dumpanswersapi.assert.invalid_argumentfor anything outside[A-Za-z0-9_-]{0,64}. Automatic dumps after a handled error name themselves within that limit, and a dump failure is logged instead of raised. - Node.js support is unchanged (
>=20 <25). As for v2.56.0, the real minimum is 20.19, which theuuiddependency requires. - Installing needs no compiler any more on
linux-x64andlinux-arm64(glibc 2.31+, i.e. every Debian image from bullseye on) and on macOS. The native modules Kuzzle maintains (dumpme, andboost-geospatial-indexandkuzzle-espresso-logic-minimizerthroughkoncorde) now ship prebuilt binaries, sonpm installno longer compiles them nor downloads Node headers. Nothing to change in a Dockerfile: a build stage that has a compiler keeps working, and a slim image without one now works too. On Alpine (musl) or another architecture, they are still compiled at install time as before. The geospatial index is also built with a fixed C++ standard. Before, the standard came from the Node version that compiled it, and on arm64 a Node 24 build could order or return geospatial results differently from a Node 20/22 one. All builds now behave like the Node 20/22 one. - Realtime with geospatial filters no longer fails after unsubscriptions. Once subscriptions with geospatial filters (
geoDistance,geoBoundingBox, …) had been removed, the geospatial index sometimes kept some of them. Matching a document against the remaining subscriptions could then throwsubfilters is not iterable: the API request that wrote the document (document:create,update, …) answered with an internal error although the document had been written, and no notification was sent for it. Removed filters are now always dropped from the index.
API clients #
These error ids changed. Each one was a wrong or generic id for a known situation; a client that switches on the old id needs to be updated.
| Situation | v2.56.0 | Now |
|---|---|---|
| A native controller crashes on an unexpected (non-Kuzzle) error | plugin.runtime.unexpected_error | core.fatal.unexpected_error (still 500) |
| Redis is disconnected | core.fatal.unexpected_error (500) | services.cache.not_connected (503) |
Elasticsearch rejects for load (es_rejected_execution_exception) | core.fatal.unexpected_error | services.storage.too_many_operations |
| A document write action sent without a body | a 500 | api.assert.body_required (400) |
| An SDK call from a plugin without user or token; an unknown strategy or plugin; a malformed WebSocket frame | a crash-shaped 500 | the documented id |
document:search with targets[].collections: null | api.assert.missing_argument | api.assert.invalid_type (still 400) |
See the error codes reference for each id.
Other observable changes:
document:exporthonourssort(an array, an object or a field name).- API keys can be deleted by
keyor byfingerprint(security:deleteApiKey, and a newDELETE /users/:userId/api-keysroute). Mind that the HTTP form puts the clear-text key in the URL. On an install upgraded from an earlier version, this works too: the internal mapping is not updated, so the key is looked up among the user's keys instead of through the index. auth:logoutclears the cookie asauthToken=(it wasauthToken=null). The anonymous user'sauth:getCurrentUserhasstrategies: [](it was[[]]).- Two realtime subscriptions to the same collection, one with
users: "out"and one withusers: "none", used to share a channel, and one of them got the other's notifications. Each now has its own channel. - The two
plugin.*.invalid_openapi_schemaerror codes are removed from the catalogue: nothing raised them.
Application and plugin developers #
TypeScript #
- The exported declarations now come from a
strictbuild. A project that compiled against v2.56.0 compiles against this version, withstricton or off, and a CI gate keeps it that way. Where v2.56.0 declared a type that the runtime did not always honour, the v2.56.0 declaration is kept for compatibility, and the JSDoc says what the runtime does. For example,getIndex({ required: false })can returnnull, andgetHeader()returnsundefinedfor a missing header. - With
skipLibCheck: false, v2.56.0's declarations failed to compile (TS7016forbluebirdandpassport) unless the project installed@types/bluebirdand@types/passportitself. Both are now dependencies of Kuzzle;@types/passportbrings the Express type packages with it (declarations only, no runtime code). - Kuzzle's own contract types.
JSONObjectis now declared by Kuzzle. It is identical to the SDK's, and the two assign to each other both ways. The new type exportsKuzzleUserandKuzzleTokenname whatrequest.context.userandrequest.context.tokenare. The exportedUseris the SDK's client-side user, asapp.sdk.security.*returns it, and the exportedTokeninterface is not the runtime token. - The SDK is still re-exported under every name it was, but by an explicit list: what the SDK adds from now on is not part of Kuzzle's API. Four client-transport re-exports are deprecated:
Kuzzle,WebSocket,HttpandKuzzleAbstractProtocol. Action: import them fromkuzzle-sdk. On the server,app.sdkis already a client.
Runtime API #
- New exports:
withLock, a distributed and reentrant Redis lock, andMutexLockLostError.withLockreplaces the deprecatedMutex; do not use both on the same key. request.response.configure({ result })sets a result without resetting the status;request.setResultstays, deprecated.request.getArrayOrCsv()names whatgetArrayLegacy()did;getArrayLegacy()is still available, deprecated.context.constructors.ESClientfollowsservices.storageEngine.majorVersion. It always built an Elasticsearch 7 client, even on an Elasticsearch 8 deployment; it now builds a client of the configured major, likeapp.storage.StorageClientalready did. Nothing changes on Elasticsearch 7. Action: on Elasticsearch 8, if a plugin uses this client, check its calls against the 8.x client (for instance, responses are no longer wrapped inbody).Protocol.entryPointis read-only for custom protocol plugins.- Deep imports from
kuzzle/dist/lib/…are not a supported API. Four modules changed their CommonJS shape (theadmin,authandsecuritycontrollers, andcluster/state), and two files are gone (util/wildcard, and the misspeltadminControlller.type). - Error reporting: a thrown value's
messageis used as before, never its serialised contents. A thrownnullgives "…: undefined" instead of crashing. - Errors of the
PluginImplementationErrorclass (theplugin.*ids, and a few others) no longer end with "This is probably not a Kuzzle error, but a problem with a plugin implementation.". When your code throws something that is not a Kuzzle error, theplugin.runtime.unexpected_errorwrapping it readsCaught an unexpected plugin error: <your message>, and its stack goes straight on to your own frames. Ids and codes are unchanged.