KeygraphHQ/shannon · error · Error
TOTP secret is empty after cleaning
Error message
TOTP secret is empty after cleaning
What it means
Thrown by base32Decode in generate-totp when the input, after uppercasing and stripping non-[A-Z2-7] characters, is empty - meaning the supplied --secret contained no valid base32 characters at all. In the CLI path this is effectively shadowed by the upstream base32-regex check (which emits 'Secret must be base32-encoded' first), so it mainly surfaces when base32Decode is called directly or in tests. The CLI wraps it as a JSON error with retryable:false and exit code 1.
Source
Thrown at apps/worker/src/scripts/generate-totp.ts:28
* generate-totp CLI
*
* Generates a TOTP code for the target's MFA.
* Based on RFC 6238 (TOTP) and RFC 4226 (HOTP).
*
* Usage:
* generate-totp --secret JBSWY3DPEHPK3PXP
*/
import { createHmac } from 'node:crypto';
// === Base32 Decoding ===
function base32Decode(encoded: string): Buffer {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
const cleanInput = encoded.toUpperCase().replace(/[^A-Z2-7]/g, '');
if (cleanInput.length === 0) {
throw new Error('TOTP secret is empty after cleaning');
}
const output: number[] = [];
let bits = 0;
let value = 0;
for (const char of cleanInput) {
const index = alphabet.indexOf(char);
if (index === -1) {
throw new Error(`Invalid base32 character: ${char}`);
}
value = (value << 5) | index;
bits += 5;
if (bits >= 8) {
output.push((value >>> (bits - 8)) & 255);
bits -= 8;View on GitHub (pinned to 1ae0a142f8)
Solutions
- Supply a non-empty base32 secret (A-Z, 2-7) via --secret.
- If scripting around generate-totp, validate with /^[A-Z2-7]+$/i before invoking.
- Restore the upstream base32 regex check if you have forked the CLI.
Example fix
# before generate-totp --secret "====" # after generate-totp --secret "JBSWY3DPEHPK3PXP"
Defensive patterns
Strategy: validation
Validate before calling
function hasBase32Content(s: string): boolean {
return /[A-Z2-7]/i.test(s);
} Try / catch
try {
const code = generateTOTP(secret);
} catch (e) {
if (e instanceof Error && /empty after cleaning/.test(e.message)) {
// prompt for a valid --secret
} else throw e;
} Prevention
- Validate the secret with /^[A-Z2-7]+$/i before calling generateTOTP/base32Decode.
- Strip padding and whitespace upstream, then assert length > 0.
- Treat empty/whitespace-only --secret as a missing-argument error in any wrapper.
When it happens
Trigger: generate-totp --secret is passed a string that, after removing non-base32 chars, leaves zero characters - e.g. a string of only spaces, padding, or non-letter symbols. In normal CLI flow the regex gate at line 135-145 catches this first with a different message; this throw fires only if that gate is bypassed.
Common situations: Calling base32Decode directly in a test with '' or '===='; an upstream code change that removes the regex pre-check; passing a secret whose only characters are base32 padding ('=') and whitespace.
Related errors
- Invalid base32 character: ${char}
- CONFIG_NOT_FOUND
- Cannot create deliverables directory at ${deliverablesDir}
- Login instructions template not found
- Failed to build login instructions: ${errMsg}
AI-assisted analysis of KeygraphHQ/shannon@1ae0a142f8 (2026-08-12).
Data as JSON: /api/errors/052920b6303d9374.
Report an issue: GitHub.