Errors
Audience: anyone who got an error from hardhat-kms or one of its provider packages.
Every error that hardhat-kms and its provider packages can raise has an entry here, with a stable id. The page is generated from their source, so it matches this version. Find an error by searching for a fixed part of its message.
{name}in a message stands for a value, such as an address or a file name.- Most errors start with the provider, the operation and the key, for example
aws, sign, key aws:alias/deployer: the provider call failed (AccessDeniedException). The tables list the part after that prefix. - Kind
config: a config validation message. Hardhat shows it in itsInvalid configerror, after the config path, for example* Config error in config.kms.keys.deployer.address: …. - Kind
reason: text that another error includes, such as the reason inthe provider returned an invalid signature ({reason}). - Kind
internal: a plainErrorthat only a bug or a broken install can cause.
Errors never include credentials or the text of an SDK error, which can carry request details. Run with DEBUG=hardhat:kms:* to see each step; see Debug output.
hardhat-kms
Configuration
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.config.timeout-type | config | Expected a number of milliseconds | timeoutMs, on a key or in kms.defaults, is not a number. | Set it to a number of milliseconds, such as 30_000. |
core.config.timeout-integer | config | Expected an integer number of milliseconds | timeoutMs, on a key or in kms.defaults, has a fraction. | Use a whole number of milliseconds. |
core.config.timeout-min | config | Expected at least 1 ms | timeoutMs, on a key or in kms.defaults, is 0 or negative. | Use a positive number of milliseconds. The default timeoutMs is 30000. |
core.config.timeout-max | config | Expected at most 2147483647 ms | timeoutMs, on a key or in kms.defaults, is larger than Node.js timers accept (2^31 - 1 ms, about 24.8 days). | Use a smaller value. |
core.config.address | config | Expected a 0x-prefixed 20-byte address, all lowercase, all uppercase or with a valid EIP-55 checksum | A key's address pin is not an address, or it is mixed-case and its EIP-55 checksum is wrong, which usually means a typo. | Copy the address from npx hardhat kms address <key>, or write it in lowercase. |
core.config.non-empty | config | Expected a non-empty string | A string field of the kms config, such as provider, region or profile, is empty. | Set the field, or remove it if it is optional. |
core.config.whitespace | config | Unexpected leading or trailing whitespace | A string field of the kms config starts or ends with whitespace. | Remove the spaces, tabs or newlines around the value. |
core.config.setting-type | config | Expected a string or a Configuration Variable | An AWS key's region or profile, or kms.defaults.aws.region, is neither a string nor configVariable(...). | Use a string, such as us-east-1 for a region, or configVariable(...). |
core.config.provider-misspelled | config | Unknown provider "{provider}". Did you mean "{suggestion}"? | A key's provider matches a built-in provider id (aws, gcp, azure) in another case, or is one edit away from one, such as AWS or gpc. | Use the id the message suggests. A third-party provider needs an id that does not look like a built-in one. |
core.config.provider-reserved | config | Provider "{provider}" is reserved for the planned {name} provider, which is not released yet (https://github.com/aelmanaa/hardhat-kms/issues/{issue}) | A key's provider is turnkey or fireblocks, in any case. hardhat-kms keeps these ids for its planned Turnkey and Fireblocks providers, so no other plugin can use them. | Follow the linked issue for the release, or use another provider. A third-party provider needs an id of its own. |
core.config.key-shape | config | Expected a key object with a `provider` field, for example { provider: "aws", keyId: "alias/deployer" } | An entry of kms.keys, or a key object in kmsAccounts, is not an object with a string provider. | Write the key as an object with a provider field and the fields of that provider. |
core.config.account-shape | config | Expected the name of a key in `kms.keys` or a key object | An entry of a network's kmsAccounts is neither a string nor an object. | List key names from kms.keys, or key objects. |
core.config.key-name | config | Key names start with a letter and use at most 64 letters, digits, `_` or `-` | A name in kms.keys has another character, starts with a digit or is too long. | Rename the key. Tasks take key names on the command line, so they are kept simple. |
core.config.simulated-balance-type | config | Expected a bigint amount of wei, for example 10n ** 18n | kms.simulatedBalance is not a bigint, for example a number or a string. | Write it as a bigint, such as 10n ** 18n for 1 ETH. |
core.config.simulated-balance-negative | config | Expected a non-negative amount of wei | kms.simulatedBalance is negative. | Use 0 or more wei. |
core.config.unknown-key-empty | config | Unknown key "{account}". `kms.keys` is empty. | A network's kmsAccounts names a key, but kms.keys defines none. | Define the key in kms.keys, or put the key object in kmsAccounts instead of its name. |
core.config.unknown-key | config | Unknown key "{account}". Known keys: {known}. | A network's kmsAccounts names a key that kms.keys does not define. Names are case-sensitive. | Use one of the names listed, or add the key to kms.keys. |
core.config.key-listed-twice | config | Key "{account}" is listed twice | A network's kmsAccounts names the same key twice. | Remove the repeated name. |
core.config.aws-endpoint | config | Expected an http or https URL without credentials, such as http://localhost:4566 | An AWS key's endpoint is not an http or https URL, or it has a user name or password, which would end up in logs and errors. | Use a plain URL. The AWS SDK takes credentials from its own chain, never from the URL. |
core.config.aws-key-id | config | Expected a key id, a key ARN, an alias name (`alias/...`) or an alias ARN | An AWS key's keyId has none of the forms AWS KMS accepts. | Use the key id, the key ARN, alias/<name> or the alias ARN, as aws kms list-aliases shows them. |
core.config.aws-region-conflict | config | Conflicts with the region in the key ARN ({region}) | An AWS key's keyId is an ARN, and its region names another region than the ARN. | Remove region: an ARN already names the region. |
core.config.azure-either-form | config | Use either `keyId` or `vaultUrl` with `keyName` (and an optional `keyVersion`), not both | An Azure key mixes the keyId form and the vaultUrl form. | Keep one form: keyId alone, or vaultUrl with keyName and an optional keyVersion. |
core.config.azure-key-id | config | Expected an https URL on an Azure Key Vault or Managed HSM host (for example `*.vault.azure.net`) with the path /keys/<name> or /keys/<name>/<version> | An Azure key's keyId is not a Key Vault or Managed HSM key URL. | Copy the key identifier from az keyvault key show (its key.kid), or from the portal. |
core.config.azure-vault-url | config | Expected an https URL on an Azure Key Vault or Managed HSM host (for example `*.vault.azure.net`), with no path | An Azure key's vaultUrl is not the https URL of a vault or Managed HSM, or it has a path. | Use the vault URL alone, such as https://my-vault.vault.azure.net, and put the key in keyName. |
core.config.azure-key-name | config | Expected 1 to 127 letters, digits or dashes | An Azure key's keyName has a character Key Vault does not allow in key names, or is too long. | Use the key's name as Key Vault shows it. |
core.config.azure-key-version | config | Expected letters and digits only | An Azure key's keyVersion is not a Key Vault version id. | Copy the version from az keyvault key list-versions, or remove keyVersion to use the current version. |
core.config.azure-workspace-id | config | Expected a Log Analytics workspace id, a GUID such as 00000000-0000-0000-0000-000000000000 | kms.audit.azure.workspaceId is not a GUID. It takes the workspace id, not the workspace name or resource id. | Copy the workspace id from az monitor log-analytics workspace show --query customerId, or from the workspace's overview in the portal. |
core.config.gcp-either-form | config | Use either `keyVersionName` or `projectId`, `location`, `keyRing`, `keyName` and `keyVersion`, not both | A Google Cloud key mixes the keyVersionName form and the parts form. | Keep one form: keyVersionName alone, or all five parts. |
core.config.gcp-key-version-name | config | Expected projects/<project>/locations/<location>/keyRings/<ring>/cryptoKeys/<key>/cryptoKeyVersions/<version> | A Google Cloud key's keyVersionName is not the resource name of a key version. | Copy the name from gcloud kms keys versions list; it ends with /cryptoKeyVersions/<version>. |
core.config.gcp-key-version-type | config | Expected a positive integer, a string or a Configuration Variable | A Google Cloud key's keyVersion is another type, or a number that is not a positive integer. | Use the version number, such as 1, a string, or configVariable(...). |
core.config.gcp-segment | config | Expected letters, digits, `_`, `.`, `:` or `-`, and not `.` or `..` | A Google Cloud key's projectId, location, keyRing or keyName has a character a resource name cannot hold. | Check the value for a typo, against the key's resource name in gcloud kms keys versions list. |
core.config.gcp-key-version | config | Expected a positive integer version | A Google Cloud key's keyVersion string is not a positive integer. | Use the version number, such as "1". |
core.config.invalid-value | error | invalid value for {path} ({display}): {problem} | A key identifier does not have its provider's format. A value from a configuration variable is checked when it is read, which config validation cannot do. {path} is the config path, such as kms.keys.deployer.keyId or --kms aws, and {display} shows a variable by its name, never its value. | Fix the value, or the variable it comes from. The part after the colon is one of the reasons below. |
core.config.aws-key-id-reason | reason | expected a key id, a key ARN, an alias name (`alias/...`) or an alias ARN | An AWS keyId has none of the forms AWS KMS accepts. | Use the key id, the key ARN, alias/<name> or the alias ARN. |
core.config.aws-region-conflict-reason | reason | the key ARN's region conflicts with `region` ({region}) | An AWS keyId is an ARN in another region than the key's region. {region} shows a region from a configuration variable by the variable's name, never its value. | Remove region, or fix the ARN. |
core.config.azure-key-id-reason | reason | expected an https URL on an Azure Key Vault or Managed HSM host (for example `*.vault.azure.net`) with the path /keys/<name> or /keys/<name>/<version> | An Azure keyId, or the URL built from vaultUrl, keyName and keyVersion, is not a key URL. | Copy the key identifier from az keyvault key show, or fix the parts. |
core.config.azure-workspace-id-reason | reason | expected a Log Analytics workspace id, a GUID | The configuration variable of kms.audit.azure.workspaceId holds something other than a GUID. | Set the variable to the workspace id from az monitor log-analytics workspace show --query customerId. |
core.config.gcp-key-version-name-reason | reason | expected projects/<project>/locations/<location>/keyRings/<ring>/cryptoKeys/<key>/cryptoKeyVersions/<version> | A Google Cloud keyVersionName, or the name built from its parts, is not a key version name. | Copy the name from gcloud kms keys versions list. |
core.config.gcp-segment-reason | reason | expected letters, digits, `_`, `.`, `:` or `-`, and not `.` or `..` | A Google Cloud projectId, location, keyRing or keyName has a character a resource name cannot hold. | Check the value for a typo, against the key's resource name in gcloud kms keys versions list. |
core.config.gcp-key-version-reason | reason | expected a positive integer version | A Google Cloud keyVersion is not a positive integer. | Use the version number, such as 1. |
The --kms option
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.kms-option.empty | error | --kms: expected one or more of aws, gcp and azure, such as --kms aws | --kms was given an empty value. | Name the providers whose environment variables hold keys, such as --kms aws or --kms aws,gcp. |
core.kms-option.unknown-provider-suggested | error | --kms {provider}: unknown provider. Did you mean "{suggestion}"? Expected aws, gcp or azure | --kms names a provider that is close to a built-in one, such as AWS or gpc. | Use the suggested id. --kms reads only the built-in providers' variables. |
core.kms-option.unknown-provider | error | --kms {provider}: unknown provider. Expected aws, gcp or azure | --kms names a provider other than aws, gcp and azure. Third-party providers are configured in kms.keys only. | Use aws, gcp or azure, or configure the key in the Hardhat config. |
core.kms-option.listed-twice | error | --kms {provider}: listed twice | --kms names the same provider twice. | Name each provider once. |
core.kms-option.list-empty | error | --kms {provider}: {list} holds no key ids | AWS_KMS_KEY_IDS or AZURE_KEY_VAULT_KEY_IDS is set, but holds only commas and spaces. | Put comma-separated key ids in it, or unset it to use the single-key variable. |
core.kms-option.not-set | error | --kms {provider}: set {single}, or {list} for several keys | --kms aws or --kms azure was given, but neither of the provider's key variables is set. | Export the variable the message names, such as AWS_KMS_KEY_ID=alias/deployer. |
core.kms-option.repeated | error | --kms {provider}: {name} repeats {first} | A list variable such as AWS_KMS_KEY_IDS holds the same key id twice. | Remove the repeated entry. |
core.kms-option.variable-not-set | error | --kms {provider}: {name} is not set | --kms gcp needs all of GCP_PROJECT_ID, GCP_LOCATION, GCP_KEY_RING, GCP_KEY_NAME and GCP_KEY_VERSION, and one is unset or empty. | Export the variable the message names. |
Provider plugins
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.provider.adapter-nothing | error | a kms.createKeyAdapter handler returned nothing for this key. Handlers must return `await next(context, key)` for keys of other providers | A provider plugin's createKeyAdapter handler returned undefined or null instead of passing the key on. | If you wrote the handler, return await next(context, key) for keys it does not handle. Otherwise report it to the provider plugin. |
core.provider.adapter-not-object | error | the adapter for this key is not an object | A provider plugin's createKeyAdapter handler returned a value that is not an adapter object. | Report it to the provider plugin; see the adapter contract in the provider guide. |
core.provider.adapter-no-describe | error | the adapter for this key has no describe() method | The adapter a provider plugin returned has no describe() method, which every adapter needs. | Report it to the provider plugin. |
core.provider.adapter-not-function | error | the adapter's {method} is not a function | The adapter has a property with a method's name, but it is not a function. | Report it to the provider plugin. |
core.provider.adapter-no-signing | error | the adapter for this key has no signing method ({methods}) | The adapter implements none of signDigest, signMessage and signTypedData. | Report it to the provider plugin. |
core.provider.adapter-no-identity | error | the adapter for this key can neither return a public key nor an address, and the key has no `address` pin | The adapter has neither getPublicKey nor getAddress, so only a pin can tell which address it signs for. | Set address on the key to the account's address. The first signature checks it. |
core.provider.adapter-describe-failed | error | the adapter's describe() failed ({errorName}) | The adapter's describe() threw. Only the error's class name is shown. | Report it to the provider plugin. |
core.provider.adapter-describe-fields | error | the adapter's describe() must return non-empty strings for {fields} | The adapter's describe() result lacks provider, pinnedId or displayId, or one is empty. | Report it to the provider plugin. |
core.provider.no-plugin | error | no plugin provides "{provider}" keys. Add the plugin for this provider to `plugins` in your Hardhat config, or check the `provider` field | No plugin in the Hardhat config handles keys of this provider. | Add the provider's plugin to plugins, or fix the key's provider field. |
core.provider.not-available | error | signing with {name} keys is not available yet (https://github.com/aelmanaa/hardhat-kms/issues/{issue}) | The provider is planned, but its adapter is not released yet. A key with a reserved id such as turnkey gets this error too when it reaches the resolved config without validation, for example through another plugin's config hook. | Follow the linked issue, or use another provider. |
core.provider.package-missing | error | {name} keys need the {package} plugin. Install it with `npm install --save-dev {package}` and add it to `plugins` in your Hardhat config | The config has a key of a built-in provider, but the provider's package is not in plugins. | Install the package the message names and add its plugin to plugins. |
core.provider.adapter-failed | error | creating the adapter failed ({errorName}) | A provider plugin threw while building the key's adapter, for example when its cloud SDK failed to load. Only the error's class name is shown, since SDK messages can carry request details. | Check that the provider's SDK is installed (npm ls), and run with DEBUG=hardhat:kms:* to see the step that failed. |
core.provider.version-mismatch | error | {package} {version} needs hardhat-kms {version}, but hardhat-kms {core} is installed. Install the same version of both, for example `npm install --save-dev hardhat-kms@{target} {package}@{target}` | A first-party provider package and hardhat-kms have different versions. They are released together, and the provider package asks for its own version of hardhat-kms as a peer dependency. Measured on new projects that install both packages: npm 11.15.0 stops the install with ERESOLVE unless it runs with --legacy-peer-deps; pnpm 12.8.1, Yarn 1.22.22 and Yarn 4.18.1 warn and install both. The check runs the first time a key of that provider is used. | Run the install command the message prints. |
core.provider.version-mismatch-no-target | error | {package} {version} needs hardhat-kms {version}, but hardhat-kms {core} is installed. Install the same version of both | As above, with a version that is not a plain major.minor.patch, such as a tag, so no install command is suggested. | Install the same version of hardhat-kms and the provider package. |
core.provider.debug-namespace-invalid | error | kmsDebug namespace {namespace} is not valid: use 1 to 64 lowercase letters, digits and -, starting with a letter | A provider package called kmsDebug from hardhat-kms/provider-utils with a namespace that is empty, longer than 64 characters, does not start with a letter, or holds another character, such as an uppercase letter, *, , or :. *, , and : would change which DEBUG patterns match it. | If you wrote the provider package, pass its provider id to kmsDebug, such as myvault. Otherwise report it to the provider package. |
core.provider.debug-namespace-reserved | error | kmsDebug namespace {namespace} belongs to hardhat-kms ({reserved}); log under the provider id instead | A provider package called kmsDebug from hardhat-kms/provider-utils with one of the namespaces hardhat-kms logs under, so its lines would pass for the core's. | If you wrote the provider package, pass its provider id to kmsDebug, such as myvault. Otherwise report it to the provider package. |
Signing
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.signer.no-identity | error | the adapter can neither return a public key nor an address; set an `address` pin | The key's adapter has neither getPublicKey nor getAddress, and the key has no address. | Set address on the key to the account's address. |
core.signer.timeout-range | error | the timeout must be an integer from 1 to 2147483647 ms, got {timeout} | A signer was created with a timeoutMs that Node.js cannot schedule. The config validation refuses such values first. | Set timeoutMs to an integer from 1 to 2147483647. |
core.signer.address-invalid | error | the configured address is invalid: {reason} | The key's address pin is not a valid address. The config validation refuses such values first. | Copy the address from npx hardhat kms address <key>, or write it in lowercase. |
core.signer.address-only | error | the provider returns only the key's address, not its public key | The provider's adapter reports an address but no public key, and the key has not signed yet in this run, which would reveal it. | Use a provider that returns the public key. An address-only provider reveals it only once the key has signed in the same run. |
core.signer.eip191-failed | error | the signature failed EIP-191 verification | The final check of a personal-message signature failed, after the signature had passed the low-level checks. The signature is not released. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.signer.eip712-failed | error | the signature failed EIP-712 verification | The final check of a typed-data signature failed, after the signature had passed the low-level checks. The signature is not released. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.signer.cannot-identify | error | the adapter cannot identify its key | The key's adapter has neither getPublicKey nor getAddress, and the key has no address. Creating the adapter refuses such a key first, so this is a guard. | Set address on the key to the account's address. |
core.signer.address-mismatch | error | the key derives to {address}, but the configured address is {expected}, so nothing was signed. The key id may now name the wrong key or a substituted one, or the pin may be wrong. Do not change the pin to match until you know why the key changed; see "When the pin fails" in the key rotation guide, which also covers a deliberate move to a new key. | The key's public key gives another address than its address pin: the key id, alias or version now names another key, by mistake or by substitution, or the pin is wrong. Nothing was signed. | Find out why the key changed before you touch the pin. If the change was a mistake, point the config back at the old key. If it was deliberate, point the old entry back at the old key and follow "Move to a new key" in the key rotation guide, which gives the new key its own entry and pin. If the pin itself is wrong, for example mistyped, do not change it until you have confirmed that the key id names the key that holds your funds and roles. Then set the pin to the address after "the key derives to" in this error, or remove the pin, run npx hardhat kms address <key> and pin what it prints. |
core.signer.digest-length | error | expected a {expected}-byte digest, got {length} bytes | Code that uses the signer asked it to sign a digest that is not 32 bytes. Nothing was sent to the provider. | Sign a 32-byte hash, such as a keccak256 digest. |
core.signer.invalid-signature | error | the provider returned an invalid signature ({reason}) | The provider returned a signature that failed the checks twice in a row. The reason is one of the signature reasons below. A key other than the configured one fails here too. | Check that the config names the key you expect, then run again. If it repeats, run with DEBUG=hardhat:kms:* and report it. |
core.signer.cannot-sign | error | the provider cannot sign a {kind} | The key's adapter has no signDigest, and no method for this kind of payload (signMessage or signTypedData). | Sign another kind of payload with this key, or use a provider that signs digests. |
core.signer.key-material | error | {reason} | The provider returned a public key or an address that the plugin refuses. The message is one of the reasons under Public keys and addresses. | Check that the key is a secp256k1 signing key, as the provider's setup guide creates it. |
core.signer.no-answer | error | no answer within {timeout} ms | The provider did not answer within the key's timeoutMs. The request may still complete at the provider; see the security model. | Check the network and the region or endpoint, or raise timeoutMs on the key or in kms.defaults. |
core.signer.cancelled | error | cancelled by the caller's abort signal | The signal given to connection.kms.getAccount aborted before or during a KMS call, so the plugin stopped waiting and returned nothing. It does not retry a cancelled call. A request that already reached the provider can still be signed there and show in its audit log, but the plugin never returns that signature. | Nothing, if the abort was meant. To sign again, get a new account with a signal that has not aborted. |
core.signer.provider-call-failed | error | the provider call failed ({errorName}) | The provider's SDK threw an error that the provider plugin does not explain. Only the error's class name is shown, since SDK messages can carry request details. | Look the class name up in the provider's setup guide, and run with DEBUG=hardhat:kms:* to see the call. |
core.signer.timed-out | reason | timed out after {timeout} ms | A provider call ran out of time. It is reported as no answer within … ms. | See core.signer.no-answer. |
core.signer.cancelled-reason | reason | cancelled by the caller | The caller's abort signal stopped a provider call. It is reported as cancelled by the caller's abort signal. | See core.signer.cancelled. |
Public keys and addresses
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.key.spki-der | reason | expected the canonical DER encoding of an uncompressed secp256k1 SubjectPublicKeyInfo | The provider returned a DER public key that is not an uncompressed secp256k1 key, for example a P-256 key. | Use a secp256k1 key, as the provider's setup guide creates it. |
core.key.jwk-key-type | reason | expected an EC key, got key type "{keyType}" | The provider returned a JSON Web Key that is not an elliptic-curve key, for example an RSA key. | Use an EC key on the secp256k1 curve. |
core.key.jwk-curve | reason | expected curve P-256K (secp256k1), got "{curve}" | The provider returned an EC JSON Web Key on another curve, such as P-256. | Use a key on the secp256k1 curve (P-256K). |
core.key.length | reason | expected a 65-byte uncompressed public key | A public key does not have the 65 bytes of an uncompressed secp256k1 point (0x04, x, y). | If a provider plugin returned it, report it to the plugin. |
core.key.not-on-curve | reason | the public key is not a point on secp256k1 | The public key's coordinates are not a point on the secp256k1 curve: it is corrupt, or from another curve. | Check that the key is a secp256k1 key. If a provider plugin returned it, report it to the plugin. |
core.key.spki-parse | reason | the public key could not be parsed as SubjectPublicKeyInfo | The provider returned a PEM or DER public key that Node.js cannot read. | If it repeats, report it to the provider plugin. |
core.key.spki-curve | reason | expected a secp256k1 key, got {keyType} {curve} | The provider returned the public key of another key type or curve. | Use a secp256k1 key, as the provider's setup guide creates it. |
core.key.coordinate-missing | reason | the key has no "{coordinate}" coordinate | The provider returned a JSON Web Key without its x or y coordinate. | If it repeats, report it to the provider plugin. |
core.key.coordinate-long | reason | the "{coordinate}" coordinate is longer than 32 bytes | A JSON Web Key coordinate is too long for secp256k1. | Check that the key is a secp256k1 key. |
core.key.invalid-address | reason | {address} is not a valid Ethereum address | A value is not 0x and 40 hex digits, or it is mixed-case with a wrong EIP-55 checksum. | Check the address for a typo, or write it in lowercase. |
Signatures
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.signature.format | reason | unsupported signature format; expected der or compact | A provider plugin returned a signature in a format the core does not read. | Report it to the provider plugin. |
core.signature.compact-length | reason | expected a 64-byte compact signature, got {length} bytes | The provider returned an r || s signature of the wrong length. | If it repeats, report it. |
core.signature.parse | reason | the {format} signature could not be parsed | The provider returned DER or compact bytes that are not a valid ECDSA signature. | If it repeats, report it. |
core.signature.scalar-type | reason | r and s must be bigints | A provider plugin returned a signature as { r, s } whose values are not bigint, for example JavaScript numbers, which cannot hold a 256-bit scalar exactly. | Report it to the provider plugin. |
core.signature.range | reason | r or s is outside the range [1, n - 1] | A signature's r or s is 0, or not below the secp256k1 curve order, so it cannot be valid. | For a signature from a provider, run again; for one given to kms verify, check that it was copied whole. |
core.signature.no-recovery | reason | the signature does not recover to the expected public key | No recovery bit gives back the key's public key: the signature is from another key, or over other data. | Check that the key id names the key you expect. |
core.signature.no-verify | reason | the signature does not verify against the expected public key | The signature does not verify against the key's public key over the digest. | Check that the key id names the key you expect. |
core.signature.hex | reason | the signature must be 0x-prefixed hex | The signature given to kms verify does not start with 0x or has a character that is not hex. | Pass the 65-byte signature as 0x and 130 hex digits. |
core.signature.rpc-length | reason | expected a 65-byte signature (r || s || v, 130 hex digits), got {digits} hex digits | The signature given to kms verify is not 65 bytes long. | Pass the whole r || s || v signature, as eth_sign and kms sign print it. |
core.signature.v | reason | v must be 0 or 1, 27 or 28, or 35 or more (EIP-155), got {v} | The last byte of the signature is not a recovery id in any form Ethereum uses. | Check that the signature was copied whole and is an Ethereum signature. |
core.signature.no-public-key | reason | no public key recovers from the signature | The signature's r is not the x coordinate of a curve point, so no signer can be recovered. | Check that the signature was copied whole. |
core.signature.digest-length | reason | expected a 32-byte digest, got {length} bytes | A signature check was asked to work on a digest that is not 32 bytes. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.signature.not-configured-address | reason | the signature does not recover to the configured address | A provider that reports only an address signed, and no recovery bit gives the address pin: the key is not the one the pin names. | Check the key id and the address pin. |
Accounts
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.accounts.same-account | error | {name} and {other} are the same account ({address}); list each key once | Two keys of the network give the same address, for example an alias and the key ARN it points to. | Remove one of them from the network's kmsAccounts or from --kms. |
core.accounts.unknown-account-hint | reason | The KMS account on this network is {list}. | A signing request named an account that is neither a KMS account nor one of the node's accounts. The plugin appends this sentence to the error from Hardhat or the node, so you can see which account it signs for. {list} is the address, or the first ten and a count. | Send from the address listed, or add the key of the account you meant to the network's kmsAccounts. |
core.accounts.unknown-account-hint-many | reason | The KMS accounts on this network are {list}. | As above, for a network with several KMS accounts. {list} holds the first ten addresses, then how many more there are. | Send from one of the addresses listed, or add the key of the account you meant to the network's kmsAccounts. |
core.accounts.already-listed | error | {name} is already {path}{named}; use one of them | A --kms key names the same KMS key as one of the network's kmsAccounts. {path} is that entry's place, such as networks.sepolia.kmsAccounts[0], and {named} adds its name in brackets when it has one. | Drop --kms, or remove the key from the network's kmsAccounts. |
core.accounts.address-bytes | error | pass the address as the hex string {address}, not as a byte array | An eth_sign, personal_sign or eth_signTypedData_v4 request names a KMS account as 20 bytes (a Buffer or a Uint8Array). Hardhat's simulated network and JSON-RPC nodes take the address only as a hex string. Nothing was signed. | Pass the address as a 0x-prefixed hex string, for example with viem's bytesToHex. |
Transactions
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.tx.not-object | error | the transaction must be an object | The first parameter of eth_sendTransaction or eth_signTransaction is not an object. | Pass the transaction as an object of eth_sendTransaction fields. |
core.tx.self-authorization-nonce | error | authorizationList[{index}] is signed by the sender for nonce {nonce}, but the final transaction requires nonce {expected}; nothing was signed | EIP-7702 increments the sender nonce before processing authorizations. Each valid self-authorization increments it again. The authorization nonce does not match. | Choose the transaction nonce explicitly and sign the self-authorization for that nonce plus 1. Use consecutive nonces for additional self-authorizations, all below 2^64 - 1. |
core.tx.self-authorization-chain | error | authorizationList[{index}] is signed by the sender for chain {requested}, but the transaction is for chain {chainId}; nothing was signed | A self-authorization names another chain. The node would skip it. | Sign the authorization for the transaction chain, or deliberately use chain 0. |
core.tx.authorization-fees | error | fee history could not be read; EIP-7702 needs EIP-1559 fees. Set maxFeePerGas and maxPriorityFeePerGas explicitly | Automatic EIP-1559 fee estimation is unavailable for a transaction with an authorization list. A legacy gas price cannot fund an EIP-7702 transaction. | Retry when the node can serve eth_feeHistory, or provide both EIP-1559 fee fields explicitly. |
core.tx.blob | error | blob transactions (EIP-4844) cannot be signed with KMS accounts; send them from another account | The transaction has blobs, blob versioned hashes, maxFeePerBlobGas or type 3 (0x3 in any case or with leading zeros, the number 3, "3" or eip4844, with or without surrounding whitespace). The plugin does not sign blob transactions. This check runs before the checks of core.tx.unsupported-type and core.tx.type-mismatch. | Send blob transactions from another account. |
core.tx.unsupported-type | error | transaction type {type} is not supported; KMS accounts sign types 0x0, 0x1, 0x2 and 0x4. Nothing was signed. | The transaction's type is a number other than 0, 1, 2, 3 or 4, such as a chain-specific type: Celo's fee-currency type 0x7b (viem's Celo formatter sends it when feeCurrency is set), zkSync's EIP-712 type 0x71, or the OP Stack deposit type 0x7e. Before, such a request was signed as a plain transaction of the type its fee fields give, without its chain-specific fields. A type counts as a number when it is a hex quantity (either case, any leading zeros), a decimal string, a non-negative integer or a bigint, with or without surrounding whitespace. Any other type, such as viem's "legacy", is ignored: the fields decide. The check runs before any request to the node. | Send the transaction from another account, or as type 0x0, 0x1, 0x2 or 0x4. |
core.tx.null-field | error | the transaction's {field} is null; set a value or leave the field out. Nothing was signed. | The transaction sets gasPrice, maxFeePerGas, maxPriorityFeePerGas, accessList or authorizationList to null, and the fill would not replace it. These fields take a value or are left out; null is neither. Before, such a request failed with a raw schema error. One null is still accepted, as Hardhat accepts it: with the network's gasPrice set to "auto", no gasPrice and the other EIP-1559 field left out, a null maxFeePerGas or maxPriorityFeePerGas is filled like an absent one. The check runs after those of core.tx.blob and core.tx.unsupported-type, and before core.tx.type-mismatch and any request to the node. {field} is the first such field, in that order. | Set the field to a value, or leave it out to let the plugin fill it. |
core.tx.type-mismatch | error | the transaction asks for type {requested}, but its fields make it type {actual}. Nothing was signed. {hint} | The transaction's type disagrees with the fee fields the caller set: gasPrice, maxFeePerGas or maxPriorityFeePerGas. The fields make it type 0x4 with an authorizationList, else 0x2 with an EIP-1559 fee field, else 0x1 with an accessList, else 0x0. For example, type 0x2 with gasPrice would be signed as legacy. Type 0x1 with gasPrice and no accessList is not a mismatch: it is signed as type 0x1 with an empty access list. A type with no fee field is not checked: the plugin fills the fees and the type follows them. A null fee or list field fails earlier, with core.tx.null-field, except a lone null EIP-1559 fee field that the automatic fee fill replaces: it counts as an EIP-1559 fee field, so type: "0x0" with maxFeePerGas: null is a mismatch. The check runs before any request to the node. {hint} says what to change, naming only the fields the transaction sets. | Set the fee fields of the type asked for (gasPrice for 0x0 and 0x1, maxFeePerGas and maxPriorityFeePerGas for 0x2 and 0x4), add an authorizationList for type 0x4, or remove type to let the fields decide. |
core.tx.no-gas | error | the transaction has no gas limit | The filled transaction has no gas limit. The fill always sets one, so this is a guard. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.tx.wrong-chain | error | the transaction is for chain {requested}, but this network is chain {chainId} | The transaction's chainId differs from the chain of the network it is sent on. Nothing was signed. | Remove chainId from the transaction, or send it on the network of that chain. |
core.tx.no-latest-block | error | the node returned no latest block | The node answered eth_getBlockByNumber("latest") with something other than a block. | Check the network's RPC URL and that the node is synced. |
core.tx.no-block-gas-limit | error | the {blockTag} block has no gasLimit | The node's block, read to cap the gas limit, has no gasLimit field. | Check the network's RPC URL, or set gas on the transaction. |
core.tx.authorization-scalar | error | authorizationList[{index}].{field} must be between 1 and the secp256k1 curve order minus 1 | An EIP-7702 authorization's r or s is 0 or not below the curve order, so the signature cannot be valid. Nothing was sent. | Sign the authorization again, for example with kms sign-auth. |
core.tx.not-plain-data | error | the transaction must be plain data (JSON values, bigints and byte arrays) | The request holds a value that structuredClone cannot copy, such as a function, a symbol or a getter that throws. | Pass plain values: strings, numbers, bigints, byte arrays, arrays and objects. |
core.tx.from-bytes | error | pass `from` as the hex string {address}, not as a byte array | The transaction's from names a KMS account as 20 bytes (a Buffer or a Uint8Array). Hardhat's simulated network and JSON-RPC nodes take from only as a hex string. Nothing was signed or sent. | Pass from as a 0x-prefixed hex string, for example with viem's bytesToHex. |
core.tx.node-answer-not-string | error | the node's {what} answer is not a string | The node answered a fee or gas request with something other than a hex string. | Check the network's RPC URL; the node may not be an Ethereum JSON-RPC node. |
core.tx.no-fee | error | the transaction has no gasPrice, maxFeePerGas or maxPriorityFeePerGas | The filled transaction has no fee field. The fill always sets one, so this is a guard Hardhat has too. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.tx.gas-price-7702 | error | an EIP-7702 transaction (authorizationList) cannot have a gasPrice | The transaction has an authorizationList and a gasPrice. EIP-7702 transactions use EIP-1559 fees. | Remove gasPrice, or set maxFeePerGas and maxPriorityFeePerGas instead. |
core.tx.both-fees | error | the transaction cannot have both gasPrice and maxFeePerGas or maxPriorityFeePerGas | The transaction mixes legacy and EIP-1559 fee fields. | Keep gasPrice alone, or maxFeePerGas with maxPriorityFeePerGas. |
core.tx.no-max-fee | error | the transaction has maxPriorityFeePerGas but no maxFeePerGas | The filled transaction has maxPriorityFeePerGas without maxFeePerGas. The fill completes the missing field, so this is a guard Hardhat has too. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.tx.no-priority-fee | error | the transaction has maxFeePerGas but no maxPriorityFeePerGas | The filled transaction has maxFeePerGas without maxPriorityFeePerGas. The fill completes the missing field, so this is a guard Hardhat has too. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.tx.creation-no-data | error | a contract creation (no `to`) needs `data` | The transaction has neither to nor data, so it would create a contract with no code. | Set to to send to an account, or data to deploy a contract. |
core.tx.both-max-fees | error | an EIP-1559 or EIP-7702 transaction needs both maxFeePerGas fields | The transaction to sign is an EIP-1559 or EIP-7702 transaction without both fee fields. The fill always sets both, so this is a guard. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.tx.no-recovery | error | the signed transaction does not recover to {from}; nothing was sent | The final check of a signed transaction found another sender than the account. The transaction is not sent. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.tx.not-from | error | the filled transaction is not from {from} | After the fill, the transaction's from is not the account that was asked to sign it. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.tx.send-waiters-full | error | Too many sends from {account} are waiting: the limit is {limit}. This send was not signed or sent. | Sends from one account on one chain run one at a time, and too many were already waiting. | Wait for earlier sends before starting more, for example by awaiting each one. |
core.tx.send-stalled | error | A send from {account} waited {seconds} s for the account's earlier sends, and none finished. This send was not signed or sent. | An earlier send from the same account is stuck, for example waiting for a KMS approval or a node that does not answer. | Find the earlier send that does not finish, and send this one again. |
core.tx.send-reentrant | error | A send from {account} was made from inside an earlier send from the same account on that chain, for example by a hook during its fill or broadcast. It would wait for itself, so it was not signed or sent. | Code that runs during a send, such as a network hook, sent from the same account again. | Send the second transaction after the first one returns. |
core.tx.raw-send-reentrant | error | A raw transaction from {account} was sent from inside an earlier send from the same account on that chain, for example by a hook during its fill or broadcast. It would wait for itself, so it was not sent. | Code that runs during a send, such as a network hook, sent a transaction that the same account signed outside the plugin, for example with a connection.kms.getAccount account. | Send the second transaction after the first one returns. |
core.tx.wallet-send-refused | error | wallet_sendTransaction is not available for the KMS account {address}. Send with eth_sendTransaction, which the plugin signs. | A wallet_sendTransaction request named a KMS account as from. The plugin sends a KMS account's transactions through eth_sendTransaction only, so it answers this method with JSON-RPC error -32601 (method not found) and does not pass it to the node. viem sends it once after an eth_sendTransaction error such as -32000, and then throws the first error. | If viem sent it, nothing: viem throws the eth_sendTransaction error instead. If your code sends it, send eth_sendTransaction. |
core.tx.wallet-send-calls-refused | error | wallet_sendCalls is not available for the KMS account {address}. Pass experimental_fallback: true to viem's sendCalls, or send each call as its own transaction. | A wallet_sendCalls (EIP-5792) request named a KMS account as from. The plugin sends a KMS account's transactions through eth_sendTransaction only, so it answers this method with JSON-RPC error -32601 (method not found) and does not pass it to the node. viem's sendCalls sends it. | Pass experimental_fallback: true to viem's sendCalls: on -32601 viem sends each call with eth_sendTransaction, and the KMS key signs each one. Or send each call yourself with sendTransaction. |
core.tx.connection-closed | error | {method}: the connection to network {network} closed before the transaction was broadcast, so nothing was sent. Send it again on an open connection | connection.close() ran while the send waited for its KMS signature or for an earlier send from the same account. A closed connection keeps no record of the account's nonces, so the plugin does not broadcast through it: a send on another connection could take the same nonce. | Wait for each send to return before you close its connection, or send again on an open connection from network.create(). |
core.tx.outcome-unknown | error | {method}: transaction {hash} was handed to the node, but no answer came back ({cause}). It may still be mined: look it up by its hash before sending another transaction. Repeating the same request within {seconds} s sends the same transaction again. | The signed transaction reached the node, but the connection failed before the node answered, so the plugin cannot tell whether it was accepted. | Look the hash up on the network. If it is not there, repeat the request: within the time the message gives, the same signed transaction is sent again. |
Library accounts
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.account.nonce-reentrant | error | A transaction of the library account {account} was started from inside an earlier send from the same account on that chain, for example by a hook during its fill or broadcast. It would wait for itself, so it was not signed or sent. | Code that runs during a send, such as a network hook, sent a transaction with a connection.kms.getAccount account of the same address. | Send the second transaction after the first one returns. |
core.account.viem-missing | error | connection.kms.getAccount needs the viem package, which could not be loaded ({reason}). Install viem in the project | getAccount returns a viem account, and viem, an optional peer dependency of hardhat-kms, is not installed in the project, or failed to load. | Install viem in the project with your package manager (for example npm install --save-dev viem, pnpm add -D viem or yarn add -D viem). Nothing else in the plugin needs it. |
core.account.viem-too-old | error | connection.kms.getAccount needs viem {floor} or later, and the project has viem {installed}. Upgrade viem to {floor} or later | The account's nonce manager relies on viem calling its reset after every failed send, and only after a consume. With viem 2.49.3 and earlier, a failed library send never resets, so the account's next send through the plugin waits up to 60 s. With viem 2.50.3 to 2.55.11, a reset can follow a send that never asked for a nonce and end another send's hold, so a send through the plugin can take a nonce that send still needs. When the project asks for a viem that no release in the peer range satisfies, such as 2.55.11 or ~2.54.0, npm 11.15.0 stops the install with ERESOLVE unless it runs with --legacy-peer-deps; pnpm 12.8.1, Yarn 1.22.22 and Yarn 4.18.1 install the older release. When the project's range overlaps the peer range but its lockfile holds an older release, such as ^2.47.6 from Hardhat's viem template with 2.52.2 locked, npm 11.15.0 installed with exit code 0 and moved viem to the newest release (2.57.2 when measured), and pnpm 12.8.1, Yarn 1.22.22, Yarn 4.18.1 and npm 11.15.0 with --legacy-peer-deps kept the locked 2.52.2. | Upgrade viem in the project to 2.55.13 or later, the floor of its peer range, with your package manager (for example npm install --save-dev viem@^2.55.13, pnpm add -D viem@^2.55.13 or yarn add -D viem@^2.55.13). |
core.account.connection-closed | error | the connection to network {network} is closed, so its KMS accounts no longer sign. Get the account from an open connection | getAccount or a method of the account it returned was called after connection.close(). A closed connection's library accounts refuse to sign, before any KMS call. | Call getAccount on an open connection, from network.create(), and use that account. |
core.account.cancelled | error | the signal given to getAccount has aborted, so the account makes no more KMS calls. Call getAccount with a new signal to sign again | getAccount, or a method of the account it returned, was called after the signal passed in its options aborted. The account refuses before any KMS call and reserves no nonce. | Call getAccount again, with a signal that has not aborted or with none. |
core.account.option | error | {name} is not an option of getAccount; the options are rawSign, allowChainZeroAuthorization and signal | The options object of getAccount has a key the plugin does not know, often a typo. It is refused so that a misspelled option is not silently ignored. | Correct or remove the option. |
core.account.field | error | {field} must be {expected} | A value given to getAccount or to one of the account's methods does not have the type or form viem gives it. Nothing was signed. | Pass the value in the form the message names, as viem's own actions pass it. |
core.account.not-kms | error | {address} is not a KMS account of network {network}.{accounts} | getAccount was given an address that none of the connection's KMS keys has. The message lists the addresses it has. | Pass one of the listed addresses, or add the key to the network's kmsAccounts. |
core.account.tx-type | error | transaction type {type} is not signed: KMS accounts sign legacy, eip2930, eip1559 and eip7702 transactions | The account's signTransaction got a transaction whose type is not one it signs, such as a chain-specific deposit type. Nothing was signed. | Send the transaction from another account, or as one of the listed types. |
core.account.tx-no-type | error | the transaction's type cannot be told from its fields: set type, gasPrice or maxFeePerGas | The transaction has no type and no fee field, so neither viem nor the plugin can tell how to encode it. viem's actions always set one. | Set type, or the fee fields of the type you want. |
core.account.tx-no-chain | error | the transaction has no chainId; a KMS account signs only for its network's chain | The account's signTransaction got a transaction without chainId. A transaction without one is valid on every chain, so it is refused. | Set chainId to the network's chain. viem's actions set it. |
core.account.tx-wrong-chain | error | the transaction is for chain {requested}, but network {network} is chain {chainId} | The transaction's chainId is another chain than the one of the connection the account comes from. Nothing was signed. | Get the account from a connection to that chain's network. |
core.account.tx-invalid | error | the transaction cannot be encoded: {reason} | The plugin's transaction encoder refused a value of the transaction, for example a number too large for its field. Nothing was signed. | Correct the value the reason names. |
core.account.serializer-failed | error | {serializer} failed on the transaction ({errorName}), so it was not signed | Before signing, the account encodes the transaction with viem's serializer, or with the chain serializer viem passed, to compare it with its own encoding. That serializer threw. | Check the transaction's fields: viem refuses values its types do not allow. |
core.account.serializer-mismatch | error | {serializer} encodes the transaction differently from hardhat-kms, so it was not signed | The account signs the transaction as the plugin encodes it, and refuses when viem's serializer, or the chain serializer viem passed, encodes other bytes. A chain with its own transaction format, such as a field the plugin does not encode, causes this. | Send the transaction from another account. If the chain uses the standard transaction format, open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the transaction's fields. |
core.account.auth-chain-zero | error | an authorization for chain 0 is valid on every chain where the account's nonce matches. Pass `allowChainZeroAuthorization: true` to getAccount to sign it | The account's signAuthorization was asked for a chain-0 authorization, which any chain accepts. | Sign for the network's chain, or get the account with allowChainZeroAuthorization: true if a chain-0 authorization is intended. |
core.account.auth-wrong-chain | error | the authorization is for chain {requested}, but network {network} is chain {chainId} | The account's signAuthorization was asked for another chain than the one of the connection the account comes from. | Get the account from a connection to that chain's network, or run kms sign-auth --chain. |
core.account.auth-no-recovery | error | the authorization does not recover to the account's address | The account checks the authorization it returns against its address, and the check failed. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
Typed data
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.typed-data.invalid | error | the typed data is invalid: {reason} | The EIP-712 typed data does not have the shape EIP-712 requires. The reason is one of the typed-data reasons below. | Fix the typed data as the reason says. |
core.typed-data.not-json | error | the typed data is not valid JSON | Typed data given as a string, as eth_signTypedData_v4 takes it, is not JSON. | Pass JSON.stringify(typedData), or the typed data object. |
core.typed-data.unreadable | error | the typed data could not be read ({errorName}); it may be nested too deeply | Reading the typed data JSON failed with something other than a syntax error, usually a stack overflow on very deep nesting. | Flatten the typed data. |
core.typed-data.chain-mismatch | error | the typed data is for chain {domainChain}, but {name} is chain {chainId}. Set `kms.allowCrossChainTypedData: true` to sign typed data for other chains | The typed data's domain.chainId is another chain than the network's. A signature for it could act on that chain. | Sign it on the network of that chain, or set kms.allowCrossChainTypedData: true if signing for other chains is intended. |
core.typed-data.plain-data | reason | the typed data must be plain data (JSON values and bigints) | The typed data holds a value that structuredClone cannot copy, such as a function, a symbol or a getter that throws. | Pass plain values. |
core.typed-data.object | reason | the typed data must be an object | The typed data is not an object. | Pass an object with types, primaryType, domain and message. |
core.typed-data.types | reason | `types` must map each type name to a list of {name, type} | The typed data's types is missing or not an object. | Give types an entry per struct, each a list of { name, type } fields. |
core.typed-data.type-fields | reason | `types.{typeName}` must be a list of {name, type} | One entry of types is not a list of { name, type } fields with string values. | Fix the struct the message names. |
core.typed-data.shape | reason | the typed data needs a string `primaryType` and object `domain` and `message` | The typed data lacks primaryType, domain or message, or one has the wrong type. | Add the missing field. |
core.typed-data.encoder | reason | {message} | The EIP-712 encoder refused the typed data, for example for an unknown type or a value that does not fit its type. The message is the encoder's own, cut to 200 characters, ending in ..., because it can quote a whole value. | Fix the type or value the message names. |
core.typed-data.unsafe-integer | reason | a number {where} is above 2^53 - 1, so JSON cannot hold it exactly; write it as a string | The typed data JSON has an integer too large for a JavaScript number, which would be rounded and signed as another value. The message names the key, cut to 40 characters, and never the number, since the file may be another one given by mistake. | Write large integers, such as uint256 amounts, as decimal or hex strings. |
Chains
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.chain.id-invalid | error | {what} is not a chain id: expected a non-negative integer, got {value} | A chain id, such as the typed data's domain.chainId or --chain, is not a non-negative integer. | Use a decimal or 0x hex integer, such as 11155111. |
core.chain.not-hex | error | the node answered eth_chainId with {answer}, not a hex quantity | The network's node answered eth_chainId with something other than a hex number. | Check the network's RPC URL; the node may not be an Ethereum JSON-RPC node. |
core.chain.mismatch | error | the network config sets chainId {configured}, but the node reports {chainId} | The network's chainId in the Hardhat config differs from the chain its node is on, so the URL may point at another chain. | Fix the network's url or its chainId. |
Tasks
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.task.unknown-network | error | unknown network "{name}" | kms accounts was run with a --network that the Hardhat config does not define. | Use a network from networks in the config. |
core.task.not-hex | error | {what} is not 0x-prefixed hex with an even number of digits | A task argument that starts with 0x is read as bytes, and this one has an odd number of digits or a character that is not hex. | Fix the hex, or drop the 0x to sign the text as UTF-8. |
core.task.typed-data-file-unreadable | error | cannot read the typed data file {file} ({errorName}) | --from-file names a file that cannot be read, for example because it does not exist. | Check the path, relative to the directory the task runs in. |
core.task.key-ambiguous | error | "{name}" names more than one key, from {sources}; rename the key in kms.keys | A key in kms.keys has the same name as a --kms key, such as AWS_KMS_KEY_ID. | Rename the key in kms.keys. |
core.task.unknown-key-none | error | unknown key "{name}". No KMS keys are configured: add them to kms.keys or a network's kmsAccounts, or pass --kms. | A kms task was given a key name, but no keys are configured. | Add keys to the config, or pass --kms with the provider's environment variables. |
core.task.unknown-key-suggested | error | unknown key "{name}". Did you mean "{suggestions}"? Known keys: {known}. | A kms task was given a key name that differs from a configured one only in case. Names are case-sensitive. | Use the suggested name. |
core.task.unknown-key | error | unknown key "{name}". Known keys: {known}. | A kms task was given a key name that is not configured. | Use one of the names listed. npx hardhat kms accounts lists every key. |
core.task.sign-no-recovery | error | the signature does not recover to the key's address | kms sign checks the signature it prints against the key's address, and the check failed. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.task.from-file-needs-data | error | --from-file requires --data | --from-file reads typed data from a file, so it works only with --data. | Add --data, or pass the message itself without --from-file. |
core.task.no-hash-with-data | error | --no-hash cannot be combined with --data | kms sign signs a raw digest with --no-hash and typed data with --data, not both. | Drop one of the two options. |
core.task.chain-options-need-data | error | --chain and --allow-cross-chain apply only to --data | The chain check of kms sign applies only to typed data. | Add --data, or drop --chain and --allow-cross-chain. |
core.task.sign-chain-and-network | error | pass --chain or --network, not both | kms sign --data compares the typed data's chain with --chain or with the network's, and both were given. | Keep one. --network can also come from the HARDHAT_NETWORK environment variable. |
core.task.no-hash-digest-length | error | --no-hash needs a {expected}-byte digest, got {length} bytes | kms sign --no-hash signs a digest as given, and the hex is not 32 bytes. | Pass a 32-byte hash as 0x and 64 hex digits. |
core.task.sign-no-chain | error | the typed data is for chain {domainChain}, and there is no chain to compare it with. Pass --network or --chain, or --allow-cross-chain to sign it for any chain | The typed data names a chain, and kms sign was given neither --network nor --chain to check it against. | Pass the chain the signature is for, or --allow-cross-chain. |
core.task.sign-chain-mismatch | error | the typed data is for chain {domainChain}, but {name} is chain {chainId}. Pass --allow-cross-chain to sign it for another chain | The typed data's domain.chainId differs from --chain or the network's chain. | Use the network or --chain of that chain, or --allow-cross-chain if signing for another chain is intended. |
core.task.verify-invalid-signature | error | invalid signature: {reason} | The signature given to kms verify cannot be read or recovered. The reason is one of the signature reasons above. | Pass the 65-byte r || s || v signature as 0x and 130 hex digits. |
core.task.verify-address-and-key | error | pass either --address or --key, not both | kms verify compares the signer with one expected account, and both options were given. | Keep one of the two. |
core.task.verify-no-signer | error | pass the expected signer with --address <address> or --key <key> | kms verify needs the account the signature should come from. | Add --address or --key. |
core.task.verify-address-invalid | error | --address: {reason} | --address is not a valid address. | Check the address for a typo, or write it in lowercase. |
core.task.tx-file-unreadable | error | cannot read the transaction file {file} ({code}) | kms sign-tx cannot read the file, for example ENOENT when it does not exist. | Check the path, relative to the directory the task runs in. |
core.task.tx-file-not-json | error | the transaction file {file} is not valid JSON at line {line}, column {column} | The file given to kms sign-tx is not JSON, for example another file given by mistake. The message names where the JSON stops being valid and quotes none of the file, which could hold a secret. | Fix the JSON. Quantities are strings, such as "0x1". |
core.task.tx-file-not-object | error | the transaction file {file} must hold one JSON object with eth_sendTransaction fields | The file holds an array, a string or another value instead of one transaction object. | Put one object in the file, such as { "to": "0x…", "value": "0x1" }. |
core.task.tx-file-checksum | error | {field} {value} has a wrong EIP-55 checksum, which usually means a typo. Check the address, or write it in lowercase. | A mixed-case address in the transaction file has a wrong EIP-55 checksum. | Check the address, or write it in lowercase. |
core.task.tx-file-unknown-fields | error | unknown transaction {fields}{hints}. The fields are those of eth_sendTransaction: {known}. | The transaction file has fields that eth_sendTransaction does not take, often a viem or ethers name such as gasLimit. {fields} is field or fields and the names; {hints} is empty or a bracketed list such as (use gas instead of gasLimit). | Rename or remove the fields; the message suggests the right name where it knows one. |
core.task.tx-file-not-quantity | error | {field} must be a hex quantity, such as "0x1", got {value} | A numeric field in the transaction file is not a 0x hex string, for example a JSON number. | Write it as a hex string. |
core.task.tx-file-type-not-quantity | error | type must be a hex quantity, such as "0x2" | The transaction file's type is not a 0x hex string. | Write it as a hex string, or remove it to let the fields decide the type. |
core.task.tx-file-unsupported-type | error | transaction type {type} is not supported; KMS accounts sign types 0x0, 0x1, 0x2 and 0x4 | The transaction file asks for a type the plugin does not sign. | Use type 0x0, 0x1, 0x2 or 0x4. |
core.task.tx-type-mismatch | error | the transaction asks for type {requested}, but its fields make it type {actual}; nothing was signed. Set the fee fields of type {requested}, or remove type. | The transaction file's type disagrees with its fee fields, for example type 0x2 with gasPrice. | Set the fee fields of the type asked for, or remove type. |
core.task.sign-tx-needs-network | error | --network is required: the transaction is filled on that network's node, and its chain id is checked against it | kms sign-tx fills the nonce, gas and fees from a node, so it needs a network. | Pass --network <name>. |
core.task.sign-tx-wrong-from | error | the transaction's from is {from}, but key {keyName} has the address {address}. Remove from, or name the key of that address. | The transaction file's from is not the address of the key the task signs with. | Remove from, or pass the key of that address. |
core.task.auth-chain-zero | error | an authorization for chain 0 is valid on every chain where the account's nonce matches. Pass --force to sign it anyway | kms sign-auth was asked for a chain-0 authorization, which any chain accepts. | Pass the chain the authorization is for, or --force if a chain-0 authorization is intended. |
core.task.auth-nonce-too-large | error | the nonce {nonce} is too large: EIP-7702 needs one below 2^64 - 1 | The authorization's nonce does not fit EIP-7702's limit. | Pass the account's real nonce, or let the task read it with --network. |
core.task.auth-no-recovery | error | the authorization does not recover to the key's address | kms sign-auth checks the authorization it prints against the key's address, and the check failed. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.task.auth-chain-and-network | error | pass --chain or --network, not both. --network can also come from the HARDHAT_NETWORK environment variable | kms sign-auth takes the chain from --chain or from the network, and both were given. | Keep one; unset HARDHAT_NETWORK if it set the network. |
core.task.auth-no-chain | error | pass --chain, or --network to sign for that network's chain | kms sign-auth needs the chain the authorization is for. | Add --chain or --network. |
core.task.auth-nonce-and-self-broadcast | error | --nonce cannot be combined with --self-broadcast | --self-broadcast changes the nonce the task reads from the node, so it has no effect with --nonce. | Pass the nonce the authorization needs with --nonce alone. |
core.task.auth-no-nonce | error | pass --nonce, or --network to read the key's pending nonce | kms sign-auth needs the account's nonce, and has no network to read it from. | Add --nonce or --network. |
core.task.auth-chain-too-large | error | --chain does not fit in 256 bits | --chain is larger than any chain id. | Pass the real chain id. |
core.task.auth-delegate-invalid | error | the delegate {delegate} is not an address: expected 0x and 40 hex digits, with a valid EIP-55 checksum if it is mixed-case | The delegate given to kms sign-auth is not an address, or its checksum is wrong. | Check the address for a typo, or write it in lowercase. |
core.task.auth-nonce-invalid | error | --nonce is not a nonce: expected a non-negative integer, got {value} | --nonce is not a decimal or 0x hex integer. | Pass a non-negative integer. |
core.task.auth-node-nonce | error | the node answered eth_getTransactionCount with something other than a nonce | The network's node did not answer eth_getTransactionCount with a hex number. | Check the network's RPC URL, or pass --nonce. |
core.task.balances-need-network | error | --balances reads balances on one network: pass --network <name> | kms accounts --balances was run without --network, so it has no node to ask. | Add --network with the network whose balances you want. |
core.task.balance-read-failed | reason | could not read the balance: {reason} | kms accounts --balances could not connect to the network, or the node did not answer eth_getBalance for the key's address. The row fails, and the task exits with code 1. | Check the network's RPC URL and that the node is reachable. The text after the colon says what failed. |
core.task.balance-not-hex | error | the node answered eth_getBalance with {answer}, not a hex quantity | The network's node answered eth_getBalance with something other than a hex number. | Check the network's RPC URL; the node may not be an Ethereum JSON-RPC node. |
core.task.check-sign-failed | reason | the sign check failed: {reason} | kms accounts --check-sign asked the key to sign a random EIP-191 message, and the KMS refused or the signature did not recover to the key's address. Reading a public key and signing need different permissions, so a key can pass the plain check and fail this one. | Give the credentials the provider's sign permission: kms:Sign on AWS, cloudkms.cryptoKeyVersions.useToSign on Google Cloud. On Azure, the identity needs read and sign on the key, as step 2 of the Azure setup guide shows: Microsoft.KeyVault/vaults/keys/read and Microsoft.KeyVault/vaults/keys/sign/action in a custom role or the built-in Key Vault Crypto User role, the get and sign key permissions in an access policy, or Microsoft.KeyVault/managedHsm/keys/read/action and Microsoft.KeyVault/managedHsm/keys/sign/action on a Managed HSM. The text after the colon says what failed. |
History
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.history.time-invalid | error | --{option} "{value}" is not a time. Use an ISO 8601 time with a time zone, such as 2026-10-02T09:00:00Z, a date such as 2026-10-02, or a duration before now, such as 30m, 6h or 7d | kms history was given a --since or --until it cannot read. A time without a time zone is refused, because it could mean any zone. | Add Z or an offset such as +02:00 to the time, or use a duration in s, m, h or d. |
core.history.range-empty | error | --since ({since}) must be before --until ({until}) | The start of the range is not before its end. Without --until, the end is now, so a --since in the future fails too. | Swap the two values, or move --since back. |
core.history.until-future | error | --until ({until}) is more than 5 minutes after now ({now}) | kms history --until names a time in the future. The log cannot hold events from then, and a range that ends later would look complete when it is not. | Leave --until out to read up to now, or check the date and its time zone. |
core.history.limit-range | error | --limit must be an integer from 1 to {max}, got {limit} | kms history --limit is 0, negative or above the maximum. | Use a smaller --limit, and narrow the range with --since and --until to see older events. |
core.history.no-reader | error | no plugin reads the audit log of "{provider}" keys, so kms history cannot list this key's sign events | No plugin in the Hardhat config implements the kms.readSignHistory hook for the key's provider. A third-party provider plugin may sign without offering a history reader. | Read the provider's audit log with its own tools, or ask the provider plugin's authors to add a reader (see the provider contract). |
core.history.no-reader-builtin | error | no installed plugin reads {name} audit logs. The reader ships in {package}; install or update it to the same version as hardhat-kms, and add it to `plugins` in your Hardhat config | The key's provider is built in, but its provider package is not in plugins, or it is a version without a history reader. | Install the package the message names, at the same version as hardhat-kms, and add its plugin to plugins. |
core.history.read-failed | error | reading the audit log failed ({errorName}) | The history reader threw an error that is not one of the plugin's own. Only its class name is shown, since SDK messages can carry request details. | Run with DEBUG=hardhat:kms:* to see the step that failed, and check the provider's read permission. |
core.history.reader-invalid | error | the history reader returned an invalid result: {problem} | A provider plugin's kms.readSignHistory handler returned something that does not follow the contract, such as an event outside the range or a value for a field it lists as not logged. | Report it to the provider plugin; see the reader contract in the provider guide. |
core.history.access-denied | error | cannot read the audit log: the credentials lack {permission}. Grant it, or run with credentials that have it | The provider refused to return log entries to the credentials in use. A reader never reports an empty history for a log it could not read. | Grant the permission the message names to the identity that runs kms history. The provider setup guides list it. |
core.history.throttled | error | the audit log kept refusing requests as too frequent ({limit}) | The provider throttled the reads, and the reader's retries did not get through. Long ranges need many requests. | Wait a minute and run again, or narrow the range with --since and --until, or lower --limit. |
core.history.access-denied-unprintable | error | cannot read the audit log: the credentials lack a permission. The history reader named it in a form that could carry an id, so it is not shown; this is a bug in the reader | The provider refused to return log entries, and the history reader passed auditLogAccessDenied a permission with characters, a URL, an ARN, a resource path or a run of digits or hex that could be an account or key id. Such a value would be printed without --show-ids. | Grant the read permission that the provider setup guide lists, and report the bug to the provider plugin. |
core.history.throttled-unprintable | error | the audit log kept refusing requests as too frequent. The history reader named the limit in a form that could carry an id, so it is not shown; this is a bug in the reader | The provider throttled the reads, and the history reader passed auditLogThrottled a limit that could carry an id. | Wait a minute and run again, or narrow the range with --since and --until, and report the bug to the provider plugin. |
core.history.reader-error | error | {message} | The history reader threw a plugin or Hardhat error. Its text is shown with key ids, workspace ids and other values that must not print masked, and without the errors it wraps, which are not masked. | Follow the message. Run with --show-ids to see it unmasked, or with DEBUG=hardhat:kms:* to see the step that failed. |
core.history.timed-out | error | reading the audit log took more than {seconds} seconds, so kms history stopped waiting | The history reader did not return within the deadline. Long ranges on a slow or throttled log need many requests. | Narrow the range with --since and --until, or lower --limit. |
Alchemy Wallet APIs
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.alchemy.input | error | checkPreparedCalls: the expected {field} is not valid. Expected {expected} | An argument of checkPreparedCalls that describes what you asked for is missing or malformed. | Pass the calls you gave prepareCalls, the key's address as sender, your chain's id, the delegate you pinned, the account's code from getCode, sponsored: true only when you passed a policy id, and maxGasCost in wei when you did not. |
core.alchemy.response-type | error | the prepared calls are {type}; only a user-operation-v070, or an array of one user-operation-v070 and at most one authorization, is accepted | prepareCalls returned another kind of response, such as paymaster-permit (ERC-20 gas), a lone authorization (undelegation) or an EntryPoint v0.6 user operation. The check cannot rebuild what those ask the key to sign. | Do not sign it. Prepare plain calls for an EIP-7702 account, with the gas paid by the account or by a policy. |
core.alchemy.malformed | error | the prepared {part} has no valid {field} | A field of the response is missing or has the wrong type. checkPreparedCalls reads the object that prepareCalls of @alchemy/wallet-apis returns, with bigint quantities and a number chain id, not the raw JSON-RPC response. | Pass the result of client.prepareCalls(...) unchanged. If you did, the response format has changed: do not sign it, and open an issue at https://github.com/aelmanaa/hardhat-kms/issues. |
core.alchemy.signature-request | error | the user operation asks for a {type} signature; only personal_sign over a raw 32-byte hash is accepted | The signature request is eth_signTypedData_v4, another type, or missing. The SDK would sign typed data as given, and the check cannot tie it to your calls. | Do not sign it. |
core.alchemy.signature-text | error | the user operation asks for personal_sign over a text message, not over a raw 32-byte hash | The personal_sign request's data is a string, or an object without a hex raw hash. The check can only tie a raw user operation hash to your calls. | Do not sign it. |
core.alchemy.chain | error | the prepared {part} is for chain {returned}, not chain {expected} | The response names another chain than the one you pass. The SDK signs an authorization for the chain the response names. | Do not sign it. Check the client's chain and the chainId you pass to checkPreparedCalls. |
core.alchemy.sender | error | the user operation's sender is {returned}, not {expected} | The user operation would run from another account than the key's address, for example because the client was given an account. | Do not sign it. Pass the key's address as sender, and leave the client's account unset for an EIP-7702 account. |
core.alchemy.factory | error | the user operation names a factory; an EIP-7702 account is never deployed by one | The user operation has factory or factoryData. An EIP-7702 account is the key's own address, delegated by an authorization, so its user operations have no factory. | Do not sign it. |
core.alchemy.hash | error | the hash in the signature request is not the EntryPoint v0.7 hash of the returned user operation on chain {chainId} | The key would sign a hash that does not belong to the user operation in the response, so the calls the check reads are not what the signature authorizes. | Do not sign it, and report it to Alchemy with the time of the call. |
core.alchemy.calls-function | error | the user operation's callData is not a Modular Account v2 execute or executeBatch call | callData calls another function of the account, such as upgradeToAndCall, a selector the account does not have, or does not decode. | Do not sign it. Only plain calls are accepted. |
core.alchemy.calls-encoding | error | the user operation's callData is not the standard encoding of its {name} call | callData decodes, but encoding the decoded call again gives other bytes: it has extra bytes or non-standard offsets. | Do not sign it. |
core.alchemy.calls-count | error | the user operation makes {returned} calls, and you requested {expected} | The prepared calls add or drop calls. | Do not sign it. Check that the calls you pass to checkPreparedCalls are the ones you gave prepareCalls. |
core.alchemy.calls-differ | error | call {index} of the user operation has another {field} than the call you requested | A prepared call goes to another address, sends another value or carries other data than the requested call at the same position. A missing value counts as 0 and missing data as 0x. | Do not sign it. Check that the calls you pass to checkPreparedCalls are the ones you gave prepareCalls, in the same order. |
core.alchemy.no-paymaster | error | a policy is set, but the user operation names no paymaster, so the account would pay the gas | You passed sponsored: true for a gas sponsorship policy, and the response has no paymaster: the policy id is wrong, its rules refused the user operation, or its budget is spent. | Check the policy id and its rules in the Alchemy dashboard, or prepare the calls without a policy and pass sponsored: false to pay the gas from the account. |
core.alchemy.not-sponsored | error | a policy is set, but the response's feePayment says the gas is not sponsored | You passed sponsored: true for a gas sponsorship policy, and the response's feePayment.sponsored is false. | Check the policy id and its rules in the Alchemy dashboard, or prepare the calls without a policy and pass sponsored: false to pay the gas from the account. |
core.alchemy.gas-cost | error | the user operation's maximum gas cost is {cost} wei, which exceeds maxGasCost {max} wei | The response's gas limits (call, verification, pre-verification and paymaster) times its maxFeePerGas exceed the maxGasCost you passed. When no paymaster pays, EntryPoint v0.7 takes that amount from the account up front, and the bundler keeps what the user operation is charged. | Do not sign it. Prepare the calls again when fees are lower, or raise maxGasCost if the cost is one you accept. |
core.alchemy.auth-nonce | error | the authorization is for nonce {returned}, not the account's nonce {expected} | The EIP-7702 authorization in the response names another nonce than the accountNonce you passed. Another transaction from the account may have moved the nonce, or the response asks for a delegation that could be applied later. | Read the account's nonce again just before prepareCalls, and prepare the calls again. |
core.alchemy.delegate | error | the authorization delegates the account to {returned}, not to {expected} | The EIP-7702 authorization in the response names another contract than the delegate you pinned. Alchemy may request an authorization to a delegate it recorded for the account without reading the chain. | Do not sign it. If you meant to move to that delegate, change the delegate you pass. |
core.alchemy.delegated-elsewhere | error | the account is delegated to {current}, not to {expected} | The account's code is an EIP-7702 delegation to another contract. Signing would run the user operation under that contract, or, with an authorization, re-delegate the account. | Clear the other delegation first, or change the delegate you pass if that contract is the one you trust. |
core.alchemy.account-code | error | the account has contract code that is not an EIP-7702 delegation | accountCode is neither empty nor 0xef0100 followed by an address, so the address holds a contract, not a key's account. | Pass the code of the key's own address, read with getCode on the chain you send on. |
core.alchemy.not-delegated | error | the account has no delegation, and the response has no authorization | The key's address has no code, so the user operation cannot run as a smart account without an authorization, and the response has none. | Read the account's code again just before prepareCalls, then prepare the calls again. |
Internal
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
core.internal.identifier-part | internal | an identifier part has no value | Joining the parts of a key id found a part without a value. Every part always has one. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.internal.wrong-provider | internal | Expected a "{expected}" key, got "{actual}" | A provider's config resolver was given another provider's key. Validation dispatches on provider, so this is a guard. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.internal.unvalidated-key | internal | Unknown key "{account}" in networks.{network}.kmsAccounts | Config resolution met a key name that validation would have refused, so the config was not validated first. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.internal.kms-option-no-value | internal | no value for {name} | A --kms key asked for a variable the option had not read. It only asks for the ones it read. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
core.internal.no-package-version | internal | {packageName}/package.json has no version | An installed package's package.json has no version, so the install is broken. | Reinstall the dependencies. If it repeats, open an issue at https://github.com/aelmanaa/hardhat-kms/issues. |
@hardhat-kms/aws
Keys
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
aws.key.spec | error | the key spec is {keySpec}, not ECC_SECG_P256K1 (secp256k1). Create the key with --key-spec ECC_SECG_P256K1 --key-usage SIGN_VERIFY | The key is not a secp256k1 key, for example an RSA or a P-256 key. | A key's spec cannot be changed. Create a new key with the command the message gives, and point the config at it. |
aws.key.usage | error | the key usage is {keyUsage}, not SIGN_VERIFY | The key was created for another usage, such as ENCRYPT_DECRYPT or GENERATE_VERIFY_MAC. | A key's usage cannot be changed. Create a new key with --key-usage SIGN_VERIFY. |
aws.key.signing-algorithm | error | the key does not support ECDSA_SHA_256 | The GetPublicKey answer does not list ECDSA_SHA_256 among the key's signing algorithms. Every ECC_SECG_P256K1 signing key supports it, so the endpoint may not be AWS KMS. | Check the key's endpoint, if one is set. Against AWS KMS itself, report it. |
aws.key.no-key-arn | error | the response has no key ARN | The GetPublicKey answer has no key ARN, which the adapter needs: it signs with the ARN, never with an alias. | Check the key's endpoint, if one is set. Against AWS KMS itself, report it. |
aws.key.no-public-key | error | the response has no public key | The GetPublicKey answer has no public key. | Check the key's endpoint, if one is set. Against AWS KMS itself, report it. |
Signing
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
aws.sign.lookup-unfinished | error | the key lookup did not finish, so there is no key ARN to sign with | The GetPublicKey call that learns the key ARN was abandoned when its time ran out. The adapter never signs without the ARN. | Run again. If it repeats, check the network and region, or raise timeoutMs. |
aws.sign.response-key | error | the response is for another key than the one requested | The Sign answer names another key ARN than the one the adapter signed with. The signature is refused. | Check the key's endpoint and any proxy. Against AWS KMS itself, report it. |
aws.sign.response-algorithm | error | the response does not use ECDSA_SHA_256 | The Sign answer names another signing algorithm than ECDSA_SHA_256. The signature is refused. | Check the key's endpoint and any proxy. Against AWS KMS itself, report it. |
aws.sign.no-signature | error | the response has no signature | The Sign answer has no signature. | Check the key's endpoint and any proxy. Against AWS KMS itself, report it. |
Connecting
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
aws.connect.no-region | error | no AWS region is configured. Set `region` on the key, `kms.defaults.aws.region`, AWS_REGION, or a region in the AWS profile, or use a key ARN | The AWS SDK found no region for the key: the key id is not an ARN, and no region is set anywhere the SDK looks. | Set region on the key or kms.defaults.aws.region, set AWS_REGION, give the profile a region, or use a key ARN. |
History
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
aws.history.key-lookup-denied | error | cannot find the key ARN: the credentials lack kms:GetPublicKey, which kms history needs for a key named by an alias or a bare key id. Grant it, or name the key by its key ARN | CloudTrail records each sign event under the key ARN. For a key named by an alias or a bare key id, kms history first calls GetPublicKey to learn the ARN, and AWS KMS refused that call. | Grant kms:GetPublicKey on the key to the identity that runs kms history (signing needs it too), or set keyId to the key ARN. |
aws.history.unreadable-event | error | CloudTrail returned a sign event that is not a readable event record, so the history cannot be shown in full | A LookupEvents answer held a Sign event whose CloudTrailEvent field is not a JSON object with a valid eventTime. The reader cannot tell whether the event belongs to the key, so it stops instead of leaving it out. | Run again. If it repeats, look at the event in the CloudTrail console, and open an issue at https://github.com/aelmanaa/hardhat-kms/issues. |
Internal
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
aws.internal.no-package-version | internal | {packageName}/package.json has no version | The installed @hardhat-kms/aws has no version in its package.json, so the install is broken. | Reinstall the dependencies. If it repeats, open an issue at https://github.com/aelmanaa/hardhat-kms/issues. |
@hardhat-kms/azure
Credentials
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
azure.credential.none | error | no Azure credential returned a token ({errorName}). Run `az login`, or set AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET for a service principal | No credential source of the chain is configured: no service principal, workload identity, managed identity or az login. | Run az login, or set the variables of a service principal or workload identity, as the setup guide describes. |
azure.credential.failed | error | a configured Azure credential could not sign in ({errorName}). Check the service principal (AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH), the workload identity or the managed identity; unset the variables of a source you do not mean to use | A credential source is configured but its sign-in failed, for example a wrong or expired AZURE_CLIENT_SECRET or a wrong AZURE_TENANT_ID. The chain does not fall through to az login after this. | Fix that source's settings, or unset the variables of a source you do not mean to use. |
azure.credential.username-password | error | AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_USERNAME and AZURE_PASSWORD are set, with no client secret or certificate, which selects username and password sign-in. hardhat-kms refuses it, because that sign-in cannot do multifactor authentication. Set AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH for a service principal, or unset AZURE_USERNAME and AZURE_PASSWORD to sign in with `az login`, workload identity or a managed identity | With these four variables set and no secret or certificate, @azure/identity's environment credential would sign in as the user with the password alone (OAuth resource owner password credentials). That sign-in cannot do multifactor authentication, and Microsoft has deprecated it. The plugin refuses before it asks any source for a token, even when az login, workload identity or a managed identity would have worked. The message names the variables, never their values. | Unset AZURE_USERNAME and AZURE_PASSWORD, or set AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH for a service principal. |
azure.credential.tenant-id | error | AZURE_TENANT_ID is not a tenant id: it may hold only letters, digits, hyphens (-) and dots (.) | The variable holds another character, such as a space or a slash. @azure/identity refuses such a tenant, so the plugin checks it when it builds the credential chain, before any request. The message never shows the value. | Set AZURE_TENANT_ID to the directory (tenant) id that az account show --query tenantId -o tsv prints, or unset it. |
azure.credential.environment-failed | reason | the service principal from the environment could not sign in | The service principal that AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH selects did not get a token, for any reason, such as a wrong secret, an unreadable certificate or a vault in a tenant that AZURE_ADDITIONALLY_ALLOWED_TENANTS does not allow. The error is named AuthenticationError, so the chain stops there and does not fall through to az login, and the plugin reports azure.credential.failed. | See azure.credential.failed. |
azure.credential.aborted | reason | The operation was aborted. | A token request was abandoned when the call's time ran out. The error is named AbortError, as the Azure SDK names its own; the plugin reports the call as no answer within … ms. | See core.signer.no-answer. |
azure.credential.managed-identity-timeout | reason | ManagedIdentityCredential: no token within {seconds} s | The managed identity endpoint gave no token in time, which happens outside Azure when the endpoint accepts a connection and never answers. The managed identity is the last source of the credential chain, so the chain then fails, and the plugin reports azure.credential.none with the chain's class name. This text is the message of that source's CredentialUnavailableError. | Outside Azure, sign in with az login or a service principal. On Azure, check that the managed identity is assigned to the resource. |
Key Vault answers
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
azure.service.unreachable | error | Key Vault could not be reached. Check the vault URL, the network and any proxy | The request got no HTTP answer. | Check the vault URL, DNS, the network and any proxy. |
azure.service.unreachable-code | error | Key Vault could not be reached ({code}). Check the vault URL, the network and any proxy | The request got no HTTP answer. The code says why, for example ENOTFOUND for DNS. | Check the vault URL, DNS, the network and any proxy. |
azure.service.401 | error | Key Vault answered {status}: the credential was not accepted | The token is for another tenant, or has expired. | Run az login again, or check AZURE_TENANT_ID. |
azure.service.403 | error | Key Vault answered {status}: the identity may not use this key. It needs to read the key and sign with it: on an RBAC vault, a role with `Microsoft.KeyVault/vaults/keys/read` and `Microsoft.KeyVault/vaults/keys/sign/action` on the key, such as a custom role or the built-in Key Vault Crypto User; in an access policy, the `get` and `sign` key permissions. See step 2 of the Azure setup guide. A disabled key or a firewall rule also gives 403 | The identity may not read the key or sign with it, the role assignment has not taken effect yet (it can take a few minutes), the key is disabled, or the vault firewall blocks the network. | Grant read and sign on the key, as step 2 of the Azure setup guide shows: on an RBAC vault, a custom role with the data actions Microsoft.KeyVault/vaults/keys/read and Microsoft.KeyVault/vaults/keys/sign/action, assigned on the key, or the built-in Key Vault Crypto User role, which also holds actions the plugin does not use; in an access policy, the get and sign key permissions; on a Managed HSM, a local role with Microsoft.KeyVault/managedHsm/keys/read/action and Microsoft.KeyVault/managedHsm/keys/sign/action. Then wait a few minutes, and check that the key is enabled and the vault's network rules. |
azure.service.404 | error | Key Vault answered {status}: the key or key version does not exist in this vault. Check the key id | The vault has no key with this name or version. | Check keyId, or keyName and keyVersion. A deleted key must be recovered first. |
azure.service.other | error | Key Vault answered {status} | Key Vault answered with another HTTP status. Only the status and Key Vault's error code are shown, since the service's message names the vault, the key and the caller. | Look the status and code up in the Key Vault documentation, and run with DEBUG=hardhat:kms:* to see the call. |
Responses
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
azure.response.key-version | error | the response is for another key version | Key Vault returned another version than the one configured or pinned. | Check that keyId names the version you mean. |
azure.response.no-public-key | error | the response has no public key | The key read from Key Vault has no JSON Web Key. | Run again; if it repeats, report it. |
azure.response.no-versioned-id | error | the response has no versioned key id | A Key Vault answer has no key id with a version, which the adapter needs to pin the version. | Run again; if it repeats, report it. |
azure.response.key | error | the response is for another key than the one requested | A Key Vault answer names another vault or key than the configured one. It is refused. | Check the vault URL and any proxy; if it repeats, report it. |
azure.response.no-signature | error | the response has no signature | The sign answer has no signature. | Run again; if it repeats, report it. |
Keys
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
azure.key.type | error | the key type is {keyType}, not EC or EC-HSM. Create an EC key on the P-256K curve | The key is not an elliptic-curve key, for example an RSA key. | A key's type cannot be changed. Create a new key with --kty EC --curve P-256K, as in the setup guide. |
azure.key.curve | error | the key curve is {curve}, not P-256K (secp256k1). Create the key with --kty EC --curve P-256K | The key is an EC key on another curve, such as P-256. | A key's curve cannot be changed. Create a new key with the command the message gives. |
azure.key.disabled | error | the key version is disabled. Enable it with `az keyvault key set-attributes {vaultOption} {vaultName} --name {keyName} --version {keyVersion} --enabled true` | The key version is disabled. | Run the command the message gives. It names the vault (--vault-name, or --hsm-name for a Managed HSM), the key and the version, because without --version the command changes the latest version, which may not be the one the config signs with. A part of the key id that comes from a configuration variable is shown as <vault-name>, <key-name> or <version>; fill it in from that variable. Run it as an identity with the keys/update permission, such as a key administrator with Key Vault Crypto Officer, or Managed HSM Crypto Officer on a Managed HSM: the two-action signing role cannot. --vault-name and --hsm-name build the vault URL from the cloud az is set to, so for a vault in another Azure cloud, run az cloud set first. |
azure.key.not-yet-valid | error | the key version is not valid before {date} | The key's activation date is in the future. | Change it with az keyvault key set-attributes --vault-name <vault-name> --name <key-name> --version <version> --not-before <date> (or --hsm-name for a Managed HSM), or use another version. Without --version the command changes the latest version. Run it as an identity with the keys/update permission, such as a key administrator with Key Vault Crypto Officer, or Managed HSM Crypto Officer on a Managed HSM: the two-action signing role cannot. --vault-name and --hsm-name build the vault URL from the cloud az is set to, so for a vault in another Azure cloud, run az cloud set first. |
azure.key.expired | error | the key version expired at {date} | The key's expiry date has passed. The plugin checks it when it reads the key and again before each signature. | Change it with az keyvault key set-attributes --vault-name <vault-name> --name <key-name> --version <version> --expires <date> (or --hsm-name for a Managed HSM), or use another version. Without --version the command changes the latest version. Run it as an identity with the keys/update permission, such as a key administrator with Key Vault Crypto Officer, or Managed HSM Crypto Officer on a Managed HSM: the two-action signing role cannot. --vault-name and --hsm-name build the vault URL from the cloud az is set to, so for a vault in another Azure cloud, run az cloud set first. |
azure.key.no-sign-operation | error | the key's permitted operations do not include sign. Set them with `az keyvault key set-attributes {vaultOption} {vaultName} --name {keyName} --version {keyVersion} --ops sign verify` | The key was created without sign in --ops. | Run the command the message gives. It names the vault (--vault-name, or --hsm-name for a Managed HSM), the key and the version, because without --version the command changes the latest version. A part of the key id that comes from a configuration variable is shown as <vault-name>, <key-name> or <version>; fill it in from that variable. Run it as an identity with the keys/update permission, such as a key administrator with Key Vault Crypto Officer, or Managed HSM Crypto Officer on a Managed HSM: the two-action signing role cannot. --vault-name and --hsm-name build the vault URL from the cloud az is set to, so for a vault in another Azure cloud, run az cloud set first. |
azure.key.not-key-url | error | the key id is not an Azure Key Vault key URL | The key id is not a Key Vault key URL. The config checks refuse such values first, so this is a guard. | Open an issue at https://github.com/aelmanaa/hardhat-kms/issues with the message and the stack trace. |
Signing
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
azure.sign.lookup-unfinished | error | the key lookup did not finish, so there is no key version to sign with | The key read that pins the version was abandoned when its time ran out. The adapter never signs without a pinned version. | Run again. If it repeats, check the network, or raise timeoutMs. |
azure.sign.signature-version | error | the signature is from another key version than the pinned one | The sign answer's kid names another version than the pinned one. The signature is refused. | Report it if it happens against Key Vault itself. |
History
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
azure.history.no-workspace | error | `kms history` reads Azure Key Vault's audit log from a Log Analytics workspace, and kms.audit.azure.workspaceId is not set. Set it to the workspace id (a GUID) that the vault's diagnostic setting sends AuditEvent logs to | Key Vault keeps no audit log of its own: a diagnostic setting on the vault sends it to a Log Analytics workspace, and the plugin needs to know which one. | Set kms.audit.azure.workspaceId to the workspace id, as the Azure setup guide's "Audit logs" section describes. az monitor log-analytics workspace show --query customerId prints it. |
azure.history.workspace-id | error | kms.audit.azure.workspaceId is not a GUID, so no Log Analytics query is sent to it | The reader puts the workspace id in the query URL, so it accepts a GUID only. The config check refuses other values first, so this means a value reached the reader without it. | Set kms.audit.azure.workspaceId to the workspace id (customerId), not its name or resource id. |
azure.history.key-id | error | the key id is not a Key Vault key URL with a host of letters, digits, . and - and a key name of letters, digits and -, so no Log Analytics query is built from it | kms history builds its query only from the checked host and key name of the key URL, so that no part can change the query. The config check refuses other values first, so this means a key reached the reader without it. | Check keyId, or vaultUrl and keyName. |
azure.history.managed-hsm | error | `kms history` does not support Managed HSM keys yet | The reader queries the AZKVAuditLogs table of Key Vault audit events. Reading the audit log of a Managed HSM is not supported yet. | Read the Managed HSM audit log in the Azure portal or with az monitor log-analytics query, or open an issue asking for Managed HSM support. |
azure.history.sovereign-cloud | error | `kms history` reads only vaults in Azure's public cloud (vault.azure.net) for now, since other clouds use another Log Analytics endpoint | Azure China and Azure Government answer Log Analytics queries on their own endpoints, which the plugin does not call yet. | Query AZKVAuditLogs with az monitor log-analytics query in that cloud, or open an issue asking for it. |
azure.history.no-table | error | the Log Analytics workspace has no AZKVAuditLogs table that this identity may read: no diagnostic setting sends Key Vault audit events to it in resource-specific mode, or the identity may not be allowed to read this table. See the Azure setup guide's "Audit logs" section | The AZKVAuditLogs table appears once a vault's diagnostic setting sends the AuditEvent category to the workspace with the resource-specific destination. A setting in the older Azure diagnostics mode writes to AzureDiagnostics, which the plugin does not read, and a setting that sends to a storage account or an event hub writes nothing here. An identity whose access is limited to other tables of the workspace may also be unable to resolve this one. | Create the diagnostic setting with --export-to-resource-specific true, check that kms.audit.azure.workspaceId names the workspace it sends to, or give the identity read access to the AZKVAuditLogs table, such as the Log Analytics Data Reader role on the workspace. |
azure.history.workspace-not-found | error | Log Analytics found no workspace with the id in kms.audit.azure.workspaceId (404 WorkspaceNotFoundError) | No workspace has this id, or the id is the workspace's resource id or name instead of its workspace id. | Set kms.audit.azure.workspaceId to the workspace's customerId, which az monitor log-analytics workspace show prints. |
azure.history.401 | error | Log Analytics answered {status}: the credential was not accepted | The token is for another tenant than the workspace's, or has expired. | Run az login again, or check AZURE_TENANT_ID. |
azure.history.read-failed | error | the Log Analytics query failed ({status}) | Log Analytics answered the query with an error. Only the HTTP status and the service's error codes are shown, since its message can name the workspace and the vault. | For a 5xx status, try again later. Run with DEBUG=hardhat:kms:* to see the call, and report a 400 with the codes it names. |
azure.history.partial | error | Log Analytics returned part of the result with an error ({code}), so the history would be incomplete | A query can succeed with only part of its result, for example when it hits a limit of the service. The reader fails rather than show part of the history as if it were all of it. | Narrow the range with --since and --until, or lower --limit, and run again. |
azure.history.unreachable | error | could not reach Log Analytics ({code}). Check the network connection, DNS and any proxy | The query got no HTTP answer, after the Azure SDK's retries. The code says why, for example ENOTFOUND for DNS. | Check that api.loganalytics.azure.com can be reached, and any HTTPS_PROXY. |
azure.history.bad-response | error | Log Analytics answered in a form this plugin does not read: {problem} | The query answer, or a KeySign row in it, lacks a field such an answer always has. The reader fails rather than show part of the history as if it were all of it. | Run again; if it repeats, report it with the problem the message names. |
Internal
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
azure.internal.no-package-version | internal | {packageName}/package.json has no version | The installed @hardhat-kms/azure has no version in its package.json, so the install is broken. | Reinstall the dependencies. If it repeats, open an issue at https://github.com/aelmanaa/hardhat-kms/issues. |
@hardhat-kms/gcp
Responses
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.response.key-version | error | the response is for another key version than the one requested | Cloud KMS answered for another key version than the one requested. The adapter does not retry this. | Run again; if it repeats, report it. |
gcp.response.no-public-key | error | the response has no public key | The getPublicKey answer has no PEM public key. | Run again; if it repeats, report it. |
gcp.response.no-signature | error | the response has no signature | The asymmetricSign answer has no signature. | Run again; if it repeats, report it. |
Keys
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.key.algorithm | error | the key version's algorithm is {algorithm}, not EC_SIGN_SECP256K1_SHA256 (secp256k1). Create the key with --purpose asymmetric-signing --default-algorithm ec-sign-secp256k1-sha256 --protection-level hsm | The key version is not a secp256k1 signing key. | A key's algorithm cannot be changed. Create a new key with the command the message gives, and point the config at it. |
Signing
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.sign.lookup-unfinished | error | the key lookup did not finish, so the key version's algorithm is not checked | The getPublicKey call that checks the key version's algorithm was abandoned when its time ran out. The adapter never signs before that check. | Run again. If it repeats, check the network, or raise timeoutMs. |
Retries
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.retry.checksum | error | {reason}, after {attempts} attempts. Data is being corrupted between this machine and Google Cloud KMS | A CRC32C checksum failed on every attempt. The reason is one of the checksum reasons below. | Check proxies or other software that rewrites HTTPS traffic between this machine and Cloud KMS. |
gcp.retry.unavailable | error | {reason}, after {attempts} attempts. Check the network connection, DNS and any proxy | Cloud KMS could not be reached, or answered UNAVAILABLE, on every attempt. The reason is gcp.reason.unreachable or gcp.reason.unavailable. | Check the network, DNS and any HTTPS_PROXY. For UNAVAILABLE without a network code, try again later. |
gcp.retry.abandoned | error | {reason} | A call failed in a way a retry could fix, but its time had run out, so it was not repeated. The message is one of the reasons below. | Run again, or raise timeoutMs. |
gcp.reason.pem-checksum | reason | the public key does not match its checksum (pemCrc32c) | The public key was corrupted on its way from Cloud KMS. | See gcp.retry.checksum. |
gcp.reason.digest-not-confirmed | reason | Google Cloud KMS did not confirm the digest's checksum (verifiedDigestCrc32c) | The checksum sent with the digest did not reach Cloud KMS. The adapter never signs on without it. | See gcp.retry.checksum. |
gcp.reason.signature-checksum | reason | the signature does not match its checksum (signatureCrc32c) | The signature was corrupted on its way from Cloud KMS. | See gcp.retry.checksum. |
gcp.reason.digest-checksum-refused | reason | Google Cloud KMS refused the digest's checksum (digestCrc32c, INVALID_ARGUMENT) | The digest was corrupted on its way to Cloud KMS, which refused it. | See gcp.retry.checksum. |
gcp.reason.unavailable | reason | Google Cloud KMS is unavailable (UNAVAILABLE) | Cloud KMS answered that it is unavailable. | Try again later. |
gcp.reason.unreachable | reason | could not reach Google Cloud KMS ({code}) | The request never reached Cloud KMS. The code says why: ENOTFOUND or EAI_AGAIN for DNS, ECONNREFUSED or ECONNRESET for the connection. | Check the network, DNS and any HTTPS_PROXY. |
Google Cloud KMS answers
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.status.not-found | error | the key version was not found (NOT_FOUND). Check the project, location, key ring, key and version | The project, location, key ring, key or version does not exist, or the caller cannot see it. | Check keyVersionName or its parts. |
gcp.status.permission-denied | error | permission denied (PERMISSION_DENIED). The caller needs cloudkms.cryptoKeyVersions.viewPublicKey and cloudkms.cryptoKeyVersions.useToSign on the key, for example through roles/cloudkms.signer and roles/cloudkms.publicKeyViewer | The identity lacks viewPublicKey or useToSign on this key. | Grant the roles the message names on the key, as in the setup guide. |
gcp.status.failed-precondition | error | the key version cannot be used (FAILED_PRECONDITION). It may be disabled, destroyed or scheduled for destruction; enable it or configure another version | The key version is disabled, scheduled for destruction or destroyed. | Enable it with gcloud kms keys versions enable, restore it first with gcloud kms keys versions restore, or configure another version. |
gcp.status.unauthenticated | error | the Google Cloud credentials were refused (UNAUTHENTICATED). Run `gcloud auth application-default login` again, or check GOOGLE_APPLICATION_CREDENTIALS | The credentials expired or were revoked: Cloud KMS or Cloud Logging refused them, or the OAuth token endpoint refused to refresh the login. | Run gcloud auth application-default login again, or check the file GOOGLE_APPLICATION_CREDENTIALS names. |
gcp.status.resource-exhausted | error | Google Cloud KMS is throttling requests (RESOURCE_EXHAUSTED). Try again later, or raise the project's Cloud KMS quota | The project hit its Cloud KMS quota. | Try again later, or ask for a higher quota. |
gcp.status.deadline-exceeded | error | Google Cloud KMS did not answer in time (DEADLINE_EXCEEDED) | The request reached its deadline, the key's timeoutMs, as the SDK reports it. | Check the network, or raise timeoutMs. |
gcp.status.other | error | the Google Cloud KMS call failed ({status}) | Cloud KMS answered with another gRPC status. Only the status name is shown, since the server's message names the project and the key. | Look the status up in the Cloud KMS documentation, and run with DEBUG=hardhat:kms:* to see the call. |
Connecting
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.connect.no-credentials | error | no Google Cloud credentials found. Run `gcloud auth application-default login`, or set GOOGLE_APPLICATION_CREDENTIALS | google-auth-library found no Application Default Credentials. | Run gcloud auth application-default login, or set GOOGLE_APPLICATION_CREDENTIALS to a credentials file. |
gcp.connect.credentials-file | error | the credentials file GOOGLE_APPLICATION_CREDENTIALS names could not be read. Check that it exists and holds Google Cloud credentials, or unset GOOGLE_APPLICATION_CREDENTIALS and run `gcloud auth application-default login` | google-auth-library could not load the file GOOGLE_APPLICATION_CREDENTIALS points at: it does not exist, is not a file, or does not hold credentials it can parse. The message leaves out the path. | Point GOOGLE_APPLICATION_CREDENTIALS at a credentials file, or unset it and run gcloud auth application-default login. |
gcp.connect.token-exchange | error | the token exchange refused the external credentials ({code}). Check the workload identity pool provider's audience and attribute condition, and that the external token has not expired | With workload identity federation, google-auth-library swaps the external token, such as a GitHub Actions OIDC token, for a Google Cloud access token at the Security Token Service, which refused it. Only the OAuth error code is shown: the error's description can repeat claims from the token. | For invalid_grant, check that the provider's audience matches the credentials file, that the attribute condition accepts the token's claims, and that the token is current. For invalid_request or unauthorized_client, recreate the credentials file from the provider. |
gcp.connect.auth-endpoint | error | getting a Google Cloud access token failed: {endpoint} answered HTTP {status}. See gcp.connect.auth-endpoint in the errors reference | While getting an access token, google-auth-library called a Google endpoint other than Cloud KMS or Cloud Logging, and that endpoint refused the call with a 4xx status. Only the HTTP status and the endpoint's name are shown: the request path can hold the project number. A 5xx, 408 or 429 is retried and reported as before. | For the token exchange, check the workload identity pool provider and the credentials file. For service account impersonation, grant the federated identity roles/iam.workloadIdentityUser on the service account. The plugin passes the key's project, so the project lookup should not happen; if it does, set GOOGLE_CLOUD_PROJECT and open an issue at https://github.com/aelmanaa/hardhat-kms/issues. |
History
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.history.key-name | error | the key version name does not split into a project, location, key ring, key and version of letters, digits, _ . : and -, so no Cloud Logging query is built from it | kms history builds its Cloud Logging filter only from the checked parts of a key version name, so that no part can change the query. The config check refuses such names first, so this means a key reached the reader without it. | Check keyVersionName or its parts. |
gcp.history.api-disabled | error | the Cloud Logging API is disabled in the project the request is billed to (SERVICE_DISABLED). Enable logging.googleapis.com there, or use another quota project | Cloud Logging refused the read because its API is not enabled in the quota project of the credentials. | Run gcloud services enable logging.googleapis.com in that project, or pick another one with gcloud auth application-default set-quota-project. |
gcp.history.read-failed | error | reading the Cloud Logging entries failed ({status}) | Cloud Logging answered entries.list with an error. Only the HTTP status and its status name are shown, since the server's message can name the project. | For 404 NOT_FOUND, check the project in the key's name. For a 5xx status, try again later. Run with DEBUG=hardhat:kms:* to see the call. |
gcp.history.unreachable | error | could not reach Cloud Logging ({code}), after {attempts} attempts. Check the network connection, DNS and any proxy | The request never reached Cloud Logging. The code says why: ENOTFOUND or EAI_AGAIN for DNS, ECONNREFUSED or ECONNRESET for the connection. | Check the network, DNS and any HTTPS_PROXY. |
gcp.history.call-timed-out | error | Cloud Logging did not answer within {seconds} seconds, after {attempts} attempts | Each entries.list call has 30 seconds, and one that gets no answer in time is retried once. A long range over many entries, or a slow network, can take longer. | Narrow the range with --since and --until, or check the network. |
gcp.history.bad-response | error | Cloud Logging answered in a form this plugin does not read: {problem} | The entries.list answer, or an AsymmetricSign entry in it, lacks a field such an answer always has. The reader fails rather than show part of the history as if it were all of it. | Run again; if it repeats, report it with the problem the message names. |
Internal
| Id | Kind | Message | Cause | Fix |
|---|---|---|---|---|
gcp.internal.no-package-version | internal | {packageName}/package.json has no version | The installed @hardhat-kms/gcp has no version in its package.json, so the install is broken. | Reinstall the dependencies. If it repeats, open an issue at https://github.com/aelmanaa/hardhat-kms/issues. |