Fix Error: error:1C80006B:Provider routines::wrong final block length in Node.js
This error occurs when decrypting data with an incorrect key, wrong IV, or corrupted ciphertext, causing the padding validation to fail. The cipher expects specific block alignment that does not match the input. Fix it by ensuring the encryption key, IV, and algorithm match between encryption and decryption.
Reading the Stack Trace
Here's what each line means:
- at Decipheriv.final (node:internal/crypto/cipher:193:29): The decipher's final() call failed because the decrypted data has invalid PKCS7 padding, indicating wrong key or corrupted data.
- at decrypt (src/utils/encryption.js:24:28): Your encryption utility at line 24 calls decipher.final() which validates padding and fails with mismatched keys.
- at TokenService.verify (src/services/tokenService.js:18:22): The token service tries to decrypt a token that was encrypted with a different key or algorithm.
Common Causes
1. Encryption key changed between encrypt and decrypt
The secret key used for encryption differs from the one used for decryption, causing padding validation to fail.
const crypto = require('crypto');
const key = process.env.ENCRYPTION_KEY; // Changed after tokens were issued
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
decipher.update(encrypted);
decipher.final(); // Fails: wrong key
2. IV not stored or transmitted with ciphertext
The initialization vector used for encryption is not saved alongside the ciphertext, so decryption uses a wrong or default IV.
function encrypt(text) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
return cipher.update(text) + cipher.final(); // IV is lost
}
3. Encoding mismatch between encrypt and decrypt
The ciphertext is encoded as hex during encryption but decoded as base64 during decryption, corrupting the data.
// Encryption: outputs hex
const encrypted = cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
// Decryption: expects base64
decipher.update(encrypted, 'base64', 'utf8'); // Wrong encoding
The Fix
Generate a random IV for each encryption and prepend it to the ciphertext. On decryption, extract the IV from the payload before decrypting. Use consistent Buffer encoding throughout to avoid corruption. Validate the payload format before attempting decryption.
const crypto = require('crypto');
function encrypt(text, key) {
const cipher = crypto.createCipheriv('aes-256-cbc', key, Buffer.alloc(16));
return cipher.update(text, 'utf8', 'hex') + cipher.final('hex');
}
function decrypt(encrypted, key) {
const decipher = crypto.createDecipheriv('aes-256-cbc', key, Buffer.alloc(16));
return decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8');
}
const crypto = require('crypto');
const ALGORITHM = 'aes-256-cbc';
const IV_LENGTH = 16;
function encrypt(text, key) {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, Buffer.from(key, 'hex'), iv);
const encrypted = Buffer.concat([cipher.update(text, 'utf8'), cipher.final()]);
return iv.toString('hex') + ':' + encrypted.toString('hex');
}
function decrypt(payload, key) {
const [ivHex, encryptedHex] = payload.split(':');
if (!ivHex || !encryptedHex) {
throw new Error('Invalid encrypted payload format');
}
const iv = Buffer.from(ivHex, 'hex');
const encrypted = Buffer.from(encryptedHex, 'hex');
const decipher = crypto.createDecipheriv(ALGORITHM, Buffer.from(key, 'hex'), iv);
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8');
}
Testing the Fix
const { encrypt, decrypt } = require('./encryption');
const crypto = require('crypto');
const key = crypto.randomBytes(32).toString('hex');
describe('Encryption', () => {
it('encrypts and decrypts a string successfully', () => {
const text = 'hello world';
const encrypted = encrypt(text, key);
const decrypted = decrypt(encrypted, key);
expect(decrypted).toBe(text);
});
it('produces different ciphertext for same input (random IV)', () => {
const text = 'test';
const a = encrypt(text, key);
const b = encrypt(text, key);
expect(a).not.toBe(b);
});
it('throws on invalid payload format', () => {
expect(() => decrypt('invalidpayload', key)).toThrow('Invalid encrypted payload format');
});
it('throws when decrypting with wrong key', () => {
const encrypted = encrypt('secret', key);
const wrongKey = crypto.randomBytes(32).toString('hex');
expect(() => decrypt(encrypted, wrongKey)).toThrow();
});
});
Run your tests:
npm test
Pushing Through CI/CD
git checkout -b fix/nodejs-crypto-error,git add src/utils/encryption.js src/utils/__tests__/encryption.test.js,git commit -m "fix: use random IV and consistent encoding for crypto operations",git push origin fix/nodejs-crypto-error
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- run: npm run lint
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
npm install bugstack-sdk
Step 2: Initialize
const { initBugStack } = require('bugstack-sdk')
initBugStack({ apiKey: process.env.BUGSTACK_API_KEY })
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Update encryption functions to use random IVs prepended to ciphertext.
- Ensure consistent encoding (hex or base64) between encrypt and decrypt.
- Run tests to verify round-trip encryption/decryption.
- Plan a migration for existing encrypted data if key format changed.
- Open a PR, merge after CI, and verify in staging.
Frequently Asked Questions
BugStack runs the fix through your existing test suite, generates additional edge-case tests, and validates that no other modules are affected before marking it safe to deploy.
BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.
Decrypt existing data with the old method, then re-encrypt with the new method. Do this in a migration script, not at runtime, and run it before deploying the new code.
AES-256-GCM is preferred because it provides authenticated encryption, detecting tampering. CBC mode requires a separate HMAC to verify integrity, adding complexity.