Troubleshooting
Fix a constructor that throws, a namespace a function cannot open, an import the build rejects, and the errors the Azion API returns.
Every symptom below is a string KV Store returns: a constructor that throws, an argument open refuses, a namespace the runtime does not find while the Azion API lists it, a value and a return type the client rejects, a delete method that is not a function, an import that fails the build, a configuration entry that creates nothing, a duplicate name answered with 400, a 405 on every request that would change a namespace, a 500 on a malformed body, a page size outside its range, and an error body a handler reads as empty.
KV constructor is private on a new Azion.KV call
A function throws KvError: KV constructor is private, use KV.open(name) instead on the line that builds the client, before any key is read or written. Both new Azion.KV() and new Azion.KV('my-namespace') produce it.
Azion.KV guards its constructor, and open is the only static member that hands back a client. The guard runs before the argument is examined, which is why the no-argument form fails the same way: there is no default namespace behind it, and the call never gets far enough to look for one. Azion.KV.open is also asynchronous, so a call that is not awaited hands the lines after it a promise where they expect a client, and the first method call on that promise throws something else entirely.
- Replace the constructor with
await Azion.KV.open(name): it is the only entry point, and it is asynchronous, so the call is awaited. - Name a namespace on every call: no form of
openresolves a default, so the argument is always the name of a namespace the account holds. - Create the namespace before the function opens it: the client cannot create one. For the operation, refer to Namespaces.
The handler then holds a client for that namespace:
Invalid name type, expected string
Azion.KV.open rejects with KvError: Invalid name type, expected string before it reaches the account. Azion.KV.open(123), Azion.KV.open(''), Azion.KV.open(null), and Azion.KV.open() each produce the same message.
open checks the type of its argument first, and it accepts a non-empty string and nothing else. A namespace carries no numeric id, so a number is never a valid argument: the name is the identifier. An empty string names nothing, and a name read from a variable that was never set arrives as undefined and fails the same check. The message describes the argument rather than the account, so a well-formed name that belongs to no namespace passes this check and fails later with a different error.
- Pass the name as a non-empty string:
Azion.KV.open('my-namespace'). - Do not pass an identifier from another product: a namespace has no
idfield, andnameis what addresses it. For the fields it carries, refer to Namespaces. - Check the variable before the call: a name built from a request or a configuration value can arrive as
undefinedor as an empty string, and either one reachesopenas an invalid type.
open then resolves the name against the account, and a name no namespace carries raises a NotFound error naming it instead.
KV namespace “my-namespace” does not exist for a namespace the Azion API lists
Azion.KV.open rejects with NotFound: KV namespace "my-namespace" does not exist inside a deployed function, while GET /v4/workspace/kv/namespaces returns that same name in results. A name no namespace carries produces the identical message, so the error does not tell the two cases apart.
open resolves the name against the runtime’s own view of the account before it returns a client, and that view is separate from the one the management API answers from. open can keep refusing a namespace that the API lists, while Azion.Storage constructs normally on the same request and env and ctx are both empty objects. That rules out a general failure to reach the stores and rules out a missing binding. Two readings remain open and neither can be settled from the function: propagation from the management API to the runtime that has not finished, and a Preview entitlement that covers the management API without covering the runtime.
- Compare the name character for character: names are case-sensitive, so the string
openreceives has to match the one the create request sent exactly, including its case. - Confirm the namespace through the Azion API:
GET /v4/workspace/kv/namespaces/{name}answers200with the namespace, or404withnamespace_not_found. For the operation, refer to Namespaces. - Confirm that the account holds KV Store Preview access: KV Store is a Preview product, and access is granted per account on request. For the channels, refer to Technical Support.
- Report it when all three hold: a namespace the API returns and the runtime refuses is not something the function can correct, so it goes to the technical support team with the namespace name and the time of the call.
The three checks separate a name you can fix yourself from a condition only the technical support team can clear, which is what the identical message otherwise hides.
INVALID_VALUE_TYPE on a put call
kv.put(key, value) rejects with INVALID_VALUE_TYPE and writes nothing. The value is a Map, a Set, a WeakMap, a WeakSet, a RegExp, or a SharedArrayBuffer.
put serializes a value it does not already hold as text or as bytes, and JSON serialization returns {} for every one of those six shapes. Writing one would therefore store an empty object under the key and resolve as a success, and the read that followed would return {} with nothing to show that the entries, the members, or the pattern had been dropped. The client refuses the value instead, so the loss surfaces at the write, on the line that caused it, rather than at a read somewhere else in the application.
- Convert a
Mapor aSetbefore you store it: aMapbecomes an array of its entries and aSetbecomes an array of its members, and both of those serialize to what they hold. - Store the parts of a
RegExprather than the object:sourceandflagsare strings, and the expression is rebuilt from them on read. - Pass an
ArrayBufferor a typed-array view for binary data:putaccepts both, and a view is written with itsbyteOffsetandbyteLengthhonored.
put then resolves, and the value comes back in the type the read asks for. For the five shapes put accepts, refer to KV client.
INVALID_MULTIPLE_GET_RETURN_TYPE on a multi-key get
kv.get(keys, returnType) rejects with INVALID_MULTIPLE_GET_RETURN_TYPE when keys is an array. The same return type on a single key is accepted, so the call looks correct next to the one beside it.
The two paths take different sets of return types. A single key reads as text, json, arrayBuffer, or stream. An array of keys reads as text or json only, because that path builds one object carrying an entry per key, and an ArrayBuffer or a ReadableStream has no place inside it. get dispatches on the type of its first argument, so whether a return type is valid depends on whether that argument is a string or an array.
- Ask for
textorjsonon an array of keys: those are the two return types that path accepts. - Read one key at a time when the value must arrive as
arrayBufferorstream: the single-key path accepts all four return types. - Expect one entry per distinct key: the array is de-duplicated before the read, so a key listed twice is read once and returns once.
The read then resolves to a plain object keyed by the key name, carrying null for a key the namespace does not hold. For that object and the four return types, refer to KV client.
Azion.KV.delete is not a function
A function throws TypeError: Azion.KV.delete is not a function on a call shaped Azion.KV.delete('my-namespace'), and Azion.KV.delete reads as undefined when the code inspects it first.
delete is a method on the client that open returns, and it removes one key. It is not a static member of Azion.KV, whose static members are length, name, prototype, and open. The two calls read alike and mean different things: kv.delete(key) removes a key from a namespace, and nothing at all removes the namespace. No interface deletes one, and that is not a permission the account is missing: the client, the Azion API, Azion CLI, and Azion Console each expose no delete path for a namespace.
- Remove the call: nothing replaces it, because no interface deletes a namespace.
- Delete the keys instead of the namespace:
await kv.delete('user-42')on a client fromAzion.KV.openremoves one key. For the method, refer to KV client. - Treat a namespace as permanent when you plan one: a name the account holds is held from then on. For the naming convention that follows from it, refer to Best practices.
The function then runs to completion, and the namespace stays on the account with whatever keys it still holds.
The build cannot resolve azion and produces no bundle
The build stops on the import line and no bundle is produced:
There is no azion:kv module. Azion.KV is a global of Azion Runtime, present in every function with no import line and no credential, and a module specifier for it has never existed. The failure is misread often, because a neighboring specifier behaves differently: azion:storage resolves in the same project with the same bundler, so a function that reaches two stores fails on one import and builds the other. The azion library does not cover it either, because the published package exports no KV entry.
- Delete the import line:
Azion.KVis reachable without it, and nothing takes its place at the top of the file. - Open the client from the global:
const kv = await Azion.KV.open('my-namespace');inside the handler. - Do not reach for
azion/kvinstead: theazionlibrary publishes no KV export, so that specifier does not resolve either.
The build then produces the bundle, and Azion.KV resolves inside the deployed handler. For the client the global exposes, refer to KV client.
A namespace declared in azion.config.js is never created
azion.config.js carries a kv entry, azion build succeeds, azion deploy reports that the application was deployed, and the account holds exactly the namespaces it held before. No warning and no error names the entry:
The entry is accepted at every stage that could reject it and acted on at none. The key is declared in the configuration’s type definitions, so an editor accepts it; the build validates it and carries it into the manifest; the deploy reads the manifest and reports success. A namespace is created by one request only, POST /v4/workspace/kv/namespaces on the Azion API. The silence is what makes this costly: a function deployed alongside that entry opens a namespace that was never created, and the symptom that reaches you is a NotFound from Azion.KV.open rather than anything pointing at the configuration.
- Create the namespace through the Azion API: one
POSTto/v4/workspace/kv/namespacescarryingnamein the body. For the request and the response it returns, refer to Namespaces. - Remove the
kventry fromazion.config.js: it creates nothing, and leaving it in place reads as though the namespace is provisioned with the function. - Confirm the namespace before you deploy the function:
GET /v4/workspace/kv/namespaceslists every name the account holds.
The namespace then exists before the first deploy, and Azion.KV.open receives a name the account holds.
Namespace already exists answers 400 and not 409
POST /v4/workspace/kv/namespaces answers 400 with the KV service envelope, and a client that branches on 409 for a collision falls through to its validation branch and reports the wrong cause:
The KV service reports every rejection of the create body under one status and one code. A name shorter than 3 characters, a name longer than 63, a name carrying a character outside ^[a-zA-Z0-9_-]+$, a missing name, and a name the account already holds all answer 400 with validation_error in code, and only message separates them. A name is unique within the account and it is permanent, so the collision is with a namespace that keeps that name from then on.
- Branch on
error.messagerather than on the status:400withvalidation_errorcovers every rejection of the body, and the message names which rule was broken. - List what the account already holds:
GET /v4/workspace/kv/namespacesreturns every name inresults. For the operation, refer to Namespaces. - Send a different name: a namespace is neither renamed nor deleted, so the name in the collision stays taken.
The create request then answers 201, and the response carries name, created_at, and last_modified.
405 Method Not Allowed on a request that changes a namespace
DELETE, PUT, or PATCH on /v4/workspace/kv/namespaces/{name} answers 405 in the platform gateway’s envelope, with 10007 in code and the text in detail:
Three operations exist on the resource and none of them changes a namespace. OPTIONS on the collection answers allow: GET, POST, HEAD, OPTIONS, and OPTIONS on a single namespace answers allow: GET, HEAD, OPTIONS. There is no rename, no deactivation, no emptying, and no delete, on the Azion API or on any other interface. A namespace is therefore permanent from the moment the create request answers 201, and the 405 is the whole answer rather than a permission to request or a header to add.
- Read the
allowheader before you write the client:OPTIONSon either path returns the methods that exist. - Remove keys rather than the namespace:
kv.delete(key)from a function removes one key at a time. For the method, refer to KV client. - Create a second namespace when the layout has to change: the first one keeps its name and its keys, and a function opens whichever one it names.
- Choose the name before the create request: the name is the identifier and it is read-only afterwards. For the rules it follows, refer to Namespaces.
The account then works with create, list, and retrieve, which is the whole surface a namespace exposes.
500 internal_error on a create request
POST /v4/workspace/kv/namespaces answers 500, while the token, the path, and the account are all correct and the same request with a different body answers 201:
Two ordinary client mistakes reach the service unhandled: a body that is not valid JSON, and a name carrying a type other than a string, such as {"name":123}. Both belong to the 400 family the endpoint uses for every other rejection of the body, and Azion tracks the mismatch as a service defect rather than behavior to write code against. What it means for you is that this status does not report an outage and does not justify a retry: the body is what to look at.
- Validate the JSON before the request is sent: a truncated body or an unquoted key produces this status rather than a parse error naming the character.
- Send
nameas a string:{"name":"my-namespace"}, and never a number or a boolean in that field. - Set
Content-Type: application/json: the create body is JSON and the header declares it. For the headers every request carries, refer to Namespaces.
The create request then answers 201 with the namespace, or 400 with a validation_error whose message names what is wrong with the name.
400 invalid_page_size on a list request
GET /v4/workspace/kv/namespaces answers 400 and returns no namespaces. A page_size of 0, of 101, and of 1000 each produce it:
page_size accepts 1 to 100 and defaults to 50. A request above the ceiling is refused rather than trimmed to it, so a client that asks for every namespace in one response receives nothing instead of the first hundred. The list pages instead, and KV Store’s envelope is not the one the rest of the Azion API v4 uses: the namespaces sit in results, and the paging fields sit under a pagination object carrying page, page_size, total_count, total_pages, has_next, and has_previous.
- Keep
page_sizebetween 1 and 100: 50 is what the endpoint uses when the parameter is absent. - Walk the pages with
has_next: sendpageonce per page untilhas_nextreadsfalse. - Do not try to narrow the response with
fields: the parameter is accepted and ignored, and every namespace comes back with all three of its fields.
The list then answers 200, and results carries one namespace per entry. For the envelope and its fields, refer to Namespaces.
A KV Store error reaches the handler with nothing in errors detail
A client reads errors[0].detail on a rejected request and logs undefined, while the response body plainly carries a message. The status is 400, 404, or 500.
Two error envelopes coexist on the same endpoint, and a handler written against one reads nothing from the other. The platform gateway emits the Azion API v4 envelope: an errors array whose entries carry a numeric code, a title, and the human text in detail. It answers the 405. The KV service emits its own: state set to error, and an error object carrying a snake_case code, the human text in message, and a details object. It answers 400, 404, and 500, which is every rejection a client meets in ordinary use.
- Read
error.messagewhen the body carriesstate:validation_error,invalid_page_size,namespace_not_found, andinternal_errorall arrive in that shape. - Keep the Azion API v4 branch for the
405: the gateway envelope is what aDELETE,PUT, orPATCHon a namespace returns. - Do not key the handler on numeric codes alone: the KV service’s codes are strings, so a branch that matches numbers skips every
400,404, and500. - Redact the body before you log it: the
404response echoes the caller’saccount_idinsidedetails.
The handler then reports the message the platform returned, whichever of the two envelopes carried it. For both shapes with every field they name, refer to Namespaces.