{"record":{"id":"e0c46a6409377ee0","repo":"beemdevelopment/Aegis","slug":"secret-is-empty-e0c46a","errorCode":null,"errorMessage":"Secret is empty","messagePattern":"Secret is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/main/java/com/beemdevelopment/aegis/otp/OtpInfo.java","lineNumber":35,"sourceCode":"    private byte[] _secret;\n    private String _algorithm;\n    private int _digits;\n\n    public OtpInfo(byte[] secret) throws OtpInfoException {\n        this(secret, DEFAULT_ALGORITHM, DEFAULT_DIGITS);\n    }\n\n    public OtpInfo(byte[] secret, String algorithm, int digits) throws OtpInfoException {\n        setSecret(secret);\n        setAlgorithm(algorithm);\n        setDigits(digits);\n    }\n\n    public abstract String getOtp() throws OtpInfoException;\n\n    protected void checkSecret() throws OtpInfoException {\n        if (getSecret().length == 0) {\n            throw new OtpInfoException(\"Secret is empty\");\n        }\n    }\n\n    public abstract String getTypeId();\n\n    public String getType() {\n        return getTypeId().toUpperCase(Locale.ROOT);\n    }\n\n    public JSONObject toJson() {\n        JSONObject obj = new JSONObject();\n\n        try {\n            obj.put(\"secret\", Base32.encode(getSecret()));\n            obj.put(\"algo\", getAlgorithm(false));\n            obj.put(\"digits\", getDigits());\n        } catch (JSONException e) {\n            throw new RuntimeException(e);","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/com/beemdevelopment/aegis/otp/OtpInfo.java#L17-L53","documentation":"OtpInfo.checkSecret is a shared guard used by concrete OTP implementations before generating a code. It throws OtpInfoException('Secret is empty') when getSecret() returns a zero-length byte array, since an OTP cannot be derived from an empty key.","triggerScenarios":"Generating an OTP (getOtp) for any OtpInfo subclass whose secret bytes are empty — typically after constructing an OtpInfo with a zero-length secret, or after decoding an empty/blank base32 secret from a URI, QR code, or imported vault entry.","commonSituations":"Importing otpauth:// URIs or backup files where the secret parameter is missing or empty; scanning a partial QR code; entries corrupted during migration; tests constructing TotpInfo/HotpInfo with empty secrets.","solutions":["Ensure a non-empty secret is set before generating OTPs: check secret.getBytes().length > 0 (or decode the base32 string and verify it isn't blank).","Reject the entry at import/parse time if its secret is empty rather than storing it.","If the entry data is valid elsewhere, re-provision the credential to obtain a real secret."],"exampleFix":"// before\nTotpInfo info = new TotpInfo(new byte[0]);\nString code = info.getOtp(); // OtpInfoException: Secret is empty\n\n// after\nbyte[] secret = Base32.decode(secretString);\nif (secret.length == 0) {\n    throw new IllegalArgumentException(\"secret must not be empty\");\n}\nTotpInfo info = new TotpInfo(secret);","handlingStrategy":"validation","validationCode":"if (secret == null || secret.length == 0) {\n    throw new IllegalArgumentException(\"OTP secret must not be empty\");\n}","typeGuard":"boolean hasSecret(OtpInfo info) {\n    return info.getSecret() != null && info.getSecret().length > 0;\n}","tryCatchPattern":"try {\n    String code = otpInfo.getOtp();\n} catch (OtpInfoException e) {\n    if (\"Secret is empty\".equals(e.getMessage())) {\n        // mark entry as invalid and ask user to re-provision\n    }\n}","preventionTips":["Validate decoded secrets at import/scan time and skip empty ones.","Never construct OtpInfo with new byte[0]; fail early at the entry-parsing layer.","Detect truncated QR scans by checking the secret parameter exists and decodes to > 0 bytes."],"tags":["otp","secret","validation","empty-value"],"backgroundTag":"empty-required-field","analyzedSha":"d6f4e5925a97e4e91593f1542085eae03432a759","analyzedAt":"2026-09-08T00:46:31.111Z","contentChangedAt":"2026-09-08T00:46:31.111Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}