Implement granular decode error messages (#2)

New exceptions for specific decode failures:
- InvalidMagicBytesError: wrong mode or not a Stegasoo image
- ReedSolomonError: image too corrupted to recover
- NoDataFoundError, ModeMismatchError: additional clarity

Web UI now shows specific, actionable error messages:
- "Try a different mode (LSB/DCT)"
- "Image too corrupted, may have been re-saved"
- "Wrong credentials - check reference photo..."

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee
2026-01-05 18:34:05 -05:00
parent 559dcd3dcf
commit f69475b406
5 changed files with 77 additions and 57 deletions

View File

@@ -112,12 +112,16 @@ from .exceptions import (
ExtractionError,
ImageValidationError,
InvalidHeaderError,
InvalidMagicBytesError,
KeyDerivationError,
KeyGenerationError,
KeyPasswordError,
KeyValidationError,
MessageValidationError,
ModeMismatchError,
NoDataFoundError,
PinValidationError,
ReedSolomonError,
SecurityFactorError,
SteganographyError,
StegasooError,
@@ -232,6 +236,10 @@ __all__ = [
"ExtractionError",
"EmbeddingError",
"InvalidHeaderError",
"InvalidMagicBytesError",
"ReedSolomonError",
"NoDataFoundError",
"ModeMismatchError",
# Constants
"FORMAT_VERSION",
"MIN_PASSPHRASE_WORDS",

View File

@@ -54,6 +54,9 @@ except ImportError:
HAS_JPEGIO = False
jio = None
# Import custom exceptions
from .exceptions import InvalidMagicBytesError, ReedSolomonError as StegasooRSError
# ============================================================================
# CONSTANTS
@@ -214,7 +217,7 @@ def _rs_decode(data: bytes) -> bytes:
pass # Errors were corrected
return bytes(decoded)
except ReedSolomonError as e:
raise ValueError(f"Reed-Solomon decoding failed: {e}") from e
raise StegasooRSError(f"Image corrupted beyond repair: {e}") from e
# ============================================================================
@@ -410,7 +413,7 @@ def _parse_header(header_bits: list) -> tuple[int, int, int]:
magic, version, flags, length = struct.unpack(">4sBBI", header_bytes)
if magic != DCT_MAGIC:
raise ValueError("Invalid DCT stego magic bytes")
raise InvalidMagicBytesError("Not a Stegasoo image or wrong mode (try LSB instead of DCT)")
return version, flags, length
@@ -461,7 +464,7 @@ def _jpegio_parse_header(header_bytes: bytes) -> tuple[int, int, int]:
raise ValueError("Insufficient header data")
magic, version, flags, length = struct.unpack(">4sBBI", header_bytes[:HEADER_SIZE])
if magic != JPEGIO_MAGIC:
raise ValueError(f"Invalid JPEG stego magic: {magic}")
raise InvalidMagicBytesError("Not a Stegasoo JPEG or wrong mode")
return version, flags, length

View File

@@ -133,6 +133,30 @@ class InvalidHeaderError(SteganographyError):
pass
class InvalidMagicBytesError(SteganographyError):
"""Magic bytes don't match - not a Stegasoo image or wrong mode."""
pass
class ReedSolomonError(SteganographyError):
"""Reed-Solomon error correction failed - image too corrupted."""
pass
class NoDataFoundError(SteganographyError):
"""No hidden data found in image."""
pass
class ModeMismatchError(SteganographyError):
"""Wrong steganography mode (LSB vs DCT)."""
pass
# ============================================================================
# FILE ERRORS
# ============================================================================