FuelLabs/fuels-ts · error · FuelError
INVALID_CREDENTIALS
INVALID_CREDENTIALS
Error message
Invalid credentials.
What it means
Thrown by the browser decrypt() function (packages/crypto/src/browser/aes-ctr.ts:81) when JSON.parse(decryptedData) fails after AES-CTR decryption completes. The Web Crypto subtle.decrypt() call itself did not throw — AES-CTR has no integrity check, so a wrong password produces valid ciphertext output that is garbage. The error is only caught when that garbage cannot be parsed as JSON, indicating the password (and thus the derived key) was wrong.
Source
Thrown at packages/crypto/src/browser/aes-ctr.ts:81
const iv = bufferFromString(keystore.iv);
const salt = bufferFromString(keystore.salt);
const secret = keyFromPassword(password, salt);
const encryptedText = bufferFromString(keystore.data);
const alg = {
name: ALGORITHM,
counter: iv,
length: 64,
};
const key = await crypto.subtle.importKey('raw', secret, alg, false, ['decrypt']);
const ptBuffer = await crypto.subtle.decrypt(alg, key, encryptedText);
const decryptedData = new TextDecoder().decode(ptBuffer);
try {
return JSON.parse(decryptedData);
} catch {
throw new FuelError(ErrorCode.INVALID_CREDENTIALS, 'Invalid credentials.');
}
};
View on GitHub (pinned to b3f37c91ac)
Solutions
- Verify the password matches the one used during encrypt().
- If the password is correct but the error persists, the keystore (data, iv, or salt fields) may be corrupted — re-create it.
- Handle the error gracefully in the UI and prompt the user to re-enter credentials.
- Note: AES-CTR lacks integrity verification; consider an authenticated mode if designing a new keystore format.
Example fix
// before
const data = await decrypt(userPassword, keystore);
// after
let data;
try {
data = await decrypt(userPassword, keystore);
} catch (e) {
if (e.code === 'invalid-credentials') {
throw new Error('Wrong password. Please try again.');
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
async function safeDecrypt(password: string, keystore: Keystore) {
try {
return { ok: true, data: await decrypt(password, keystore) };
} catch (e) {
if (e instanceof FuelError && e.code === 'invalid-credentials') {
return { ok: false, error: 'Wrong password' };
}
throw e;
}
} Try / catch
try {
const data = await decrypt(password, keystore);
} catch (e) {
if (e instanceof FuelError && e.code === 'invalid-credentials') {
// Password is wrong, or keystore data/iv/salt is corrupted
// Prompt user to re-enter password
}
throw e;
} Prevention
- Always handle INVALID_CREDENTIALS in the UI with a clear 'wrong password' message.
- Note: AES-CTR has no integrity check, so there is a negligible probability that wrong-password garbage parses as valid JSON.
- If the password is correct but the error persists, the keystore may be corrupted — re-create it.
- Store the keystore fields (data, iv, salt) together and atomically to prevent partial corruption.
When it happens
Trigger: Calling decrypt(password, keystore) with the wrong password. The wrong password produces a wrong PBKDF2 key, which decrypts the ciphertext into garbage bytes, which fail JSON.parse. NOTE: there is a small probability (negligible but nonzero) that garbage happens to be valid JSON, in which case no error is thrown and incorrect data is returned.
Common situations: User enters the wrong password to unlock a keystore/wallet; keystore was encrypted with a different password than provided; keystore data or salt/iv is corrupted; migrating keystores between systems with different password encoding.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- INVALID_CREDENTIALS
- INVALID_PASSWORD
- ENV_DEPENDENCY_MISSING
- INVALID_PUBLIC_KEY
- MISSING_REQUIRED_PARAMETER
AI-assisted analysis of FuelLabs/fuels-ts@b3f37c91ac (2026-08-12).
Data as JSON: /api/errors/ae70ea7b3954a836.
Report an issue: GitHub.