ARCHIVED DOCUMENT — All issues documented below have been RESOLVED as of v0.10.10.This document is preserved for historical reference and to demonstrate the evolution of the codebase’s type safety and validation patterns.Archive Date: 2026-01-20 | Original Date: 2026-01-11 | Status: ✅ All 11 issue categories resolved
Resolution Summary
All type validation issues identified during the v0.10.7 investigation have been systematically resolved across subsequent releases:
High Impact Issues (3/3 RESOLVED)
Medium Impact Issues (3/3 RESOLVED)
Low Impact Issues (5/5 RESOLVED)
Key Improvements Across All Versions
v0.10.8 - Systematic Validation Overhaul:
- Dict input validation through Pydantic (50+ methods)
- Config hardening (scheme, port, verify_ssl)
- Protected path blocking with StashConfigurationError
- Bool return pattern standardization (40 methods)
v0.10.9 - Documentation:
- Updated CHANGELOG with v0.10.8 release notes
v0.10.10 - None-Handling Refinement:
- Fixed 25 instances of incorrect
dict.get("field", None) usage
- GraphQL now correctly distinguishes explicit null from missing fields
- 100% branch coverage achieved for validation patterns
Unknown Versions - Additional Fixes:
- ID validation with positive integer checks
- MIME type validation for image files
- Direction parameter validation
- Image index range validation
- Timestamp type system with Pydantic validators
Architectural Philosophy Evolution
This document demonstrates the codebase’s shift from “fail late at GraphQL” to “fail early with clear errors”:
Before (v0.10.7 and earlier):
- Accept any dict structure → silent GraphQL failures
- No validation on IDs, ranges, or types
- Inconsistent patterns across 40+ methods
- Type coercion without validation
After (v0.10.10+):
- Pydantic validation for all dict inputs
- Positive validation for IDs and ranges
- Consistent
is True pattern for booleans
- Type validation with clear error messages
- Duck typing for flexibility (Logger)
Original Document Follows
This document tracks non-critical type validation issues discovered during the v0.10.7 TTL bug investigation. These issues follow similar patterns but have lower severity or impact.
Status: Documented for future work ALL RESOLVED
Priority: Medium to Low COMPLETED
Related: See CHANGELOG.md v0.10.7-v0.10.10 for all fixes
Table of Contents
- High Impact Issues
- Medium Impact Issues
- Low Impact Issues
- Recommendations
High Impact Issues
1. Dict Structure Validation (RESOLVED v0.10.8)
Status: ✅ RESOLVED - All 50+ methods validate dict inputs through Pydantic
Pattern: Methods accept SomeType | dict[str, Any] but perform no structural validation.
Files Affected:
stash_graphql_client/client/mixins/marker.py line 211
stash_graphql_client/client/mixins/image.py line 207
stash_graphql_client/client/mixins/gallery.py line 435
- 17+ additional methods across other mixins
Problem (Historical):
Impact: Silent failures at GraphQL layer if dict has wrong keys or missing required fields.
Resolution (v0.10.8):
Benefits:
- Type checking prevents passing wrong types
- Pydantic validation catches field type errors
- Clear error messages for missing required fields
- Prevents silent GraphQL failures
Severity: N/A (resolved)
2. Path/String Confusion (Fixed in v0.10.8)
Status: ✅ RESOLVED - Proper error handling for invalid paths
File: stash_graphql_client/types/performer.py lines 261-269
Resolution (v0.10.8):
Benefits:
- Catches invalid path characters (null bytes, etc.) with clear error message
- Validates path exists and is a file (not directory)
- Proper error chaining with
from e
- Updated docstring to reflect ValueError for invalid paths
Severity: N/A (resolved)
2.2 SystemQueryClientMixin.directory() - ✅ RESOLVED
Status: ✅ RESOLVED - Type annotation improved, Path support added
File: stash_graphql_client/client/mixins/system_query.py lines 161-198
Resolution (v0.10.8):
Benefits:
- Type annotation now accepts
str | Path | None
- Docstring clarifies Path object support
- Automatic Path → string conversion for GraphQL
- No validation needed (Stash server validates directory existence)
Severity: N/A (resolved)
2.3 Config Path Fields - ✅ RESOLVED
Status: ✅ RESOLVED - Protected with StashConfigurationError
File: stash_graphql_client/types/config.py lines 206-244
Problem (Historical): ConfigGeneralInput allowed modifying 12 critical server filesystem paths, which could corrupt Stash installation during testing or automation.
Resolution (v0.10.8): Added Pydantic model validator that rejects any path modifications:
New Error Type: Created StashConfigurationError in errors.py for configuration safety
Benefits:
- Prevents accidental path corruption during testing (addresses reported issue)
- Clear error message directing users to Stash web UI
- Protects 12 critical server-side paths from client modification
- Maintains safety even if paths are passed via dict or ConfigGeneralInput
Severity: N/A (resolved)
3. CIMultiDict Type Mismatch (Fixed in v0.10.8)
Status: ✅ RESOLVED - False positive, intentional design improved
File: stash_graphql_client/context.py lines 70, 106
Original Concern: CIMultiDict was stored and passed to StashClient, causing type checker mismatch.
Resolution:
- v0.8.2 introduced CIMultiDict to fix case-sensitivity bug (lowercase
scheme/host/port/apikey now work)
- v0.10.8 improved implementation: CIMultiDict now used only for normalization, converted to regular dict with canonical keys
- Users can pass any case (
scheme, Scheme, SCHEME), stored as canonical Scheme
- Type safety restored: StashClient receives proper
dict[str, Any]
- Preserves v0.8.2 case-insensitive input behavior
Implementation:
Severity: N/A (not a bug, design improvement applied)
Medium Impact Issues
4. Bool Conversion Pattern Inconsistencies (Fixed in v0.10.8)
Status: ✅ RESOLVED - Standardized on Pattern 1c across all 40 methods
Problem (Historical): Three different patterns existed across 40 methods:
Pattern 1: bool(result.get(..., False)) - redundant bool() wrapper
Pattern 2: result.get(..., False) - no bool() wrapper (returns Any)
Pattern 3: bool(result[...]) - bracket access, could raise KeyError
Resolution (v0.10.8): All 40 methods now use Pattern 1c (identity checking):
Why Pattern 1c is best:
- Identity checking: Uses
is True for exact boolean matching (not truthy values)
- No coercion: Doesn’t convert 1, “yes”, or other truthy values to True
- Handles None:
result.get("key") returns None by default, None is True evaluates to False
- Most Pythonic: Explicit is better than implicit
Files Updated: 15 mixin files (gallery, group, config, file, scene, tag, image, performer, studio, marker, plugin, jobs, metadata, filter, scraper)
Severity: N/A (resolved)
5. _parse_obj_for_ID() Int Conversion (RESOLVED)
Status: ✅ RESOLVED - ID validation with positive checks and error handling
File: stash_graphql_client/client/base.py lines 505-516
Problem (Historical):
Issues:
- No validation that ID is positive
.get() returns None if key missing, then int(None) raises TypeError
- Bracket notation
param["stored_id"] after .get() check is redundant
Resolution:
Benefits:
- Validates IDs are positive integers
- Proper TypeError and ValueError handling
- Error chaining with
from e
- Clean logic without redundant patterns
Severity: N/A (resolved)
6. Optional/None Handling Issues (Resolved in v0.10.8, v0.10.10)
6.1 Config Dict Value Types - ✅ RESOLVED with Duck Typing
Status: ✅ RESOLVED - Proper validation added with duck typing support
File: stash_graphql_client/client/base.py lines 136-163
Resolution (v0.10.8):
- Scheme: ✅ Validated (must be “http” or “https”)
- Port: ✅ Already validated in v0.10.7 (int or numeric string, 0-65535)
- verify_ssl: ✅ Already validated in v0.10.8 (bool or string coercion)
- Logger: ✅ Duck typing - documented in docstring, no validation needed
- Host: ✅ No validation needed - can be IPv4, IPv6, shortname, or FQDN
Implementation:
Logger Duck Typing Rationale:
- Logger only needs
.debug(), .info(), .warning(), .error() methods
- Validates
isinstance(logger, logging.Logger) would prevent using loguru, structlog, etc.
- Duck typing provides flexibility while maintaining safety
- Documented in docstring: “duck-typed: must have .debug(), .info(), .warning(), .error() methods”
Severity: N/A (resolved with appropriate validation and duck typing)
6.2 marker_filter Parameter - ℹ️ FALSE POSITIVE
Status: ℹ️ FALSE POSITIVE - No issue, GraphQL handles None correctly
File: stash_graphql_client/client/mixins/marker.py line 74
Analysis:
Why it’s OK:
- GraphQL transparently handles None values in variables
- The
execute() method filters out None values before sending to server
- No TypeError occurs - this is expected behavior
Severity: N/A (false positive)
Low Impact Issues
7. String Encoding/Bytes Edge Case (RESOLVED)
Status: ✅ RESOLVED - MIME type validation added
File: stash_graphql_client/types/performer.py lines 268-272
Problem (Historical):
Issue: Default MIME type is “image/jpeg” but file might be PNG, GIF, etc.
Impact: Very low (data URLs usually work regardless, browsers are forgiving)
Resolution:
Benefits:
- No longer defaults to “image/jpeg”
- Validates file has image MIME type
- Case-insensitive suffix matching
Severity: N/A (resolved)
8. Enum/Literal String Validation
8.1 Direction Parameter Not Validated (RESOLVED)
Status: ✅ RESOLVED - Direction validation with enum and string support
Files: Multiple mixins (tag.py, performer.py, etc.)
Problem (Historical):
- Documentation says “direction: SortDirectionEnum (ASC/DESC)”
- But parameter typed as
str | None
- No validation that value is actually “ASC” or “DESC”
Impact: User passes “ASCENDING” or “desc” (lowercase) → silent failure at GraphQL.
Resolution (base.py:552-568):
Benefits:
- Accepts SortDirectionEnum instances
- Validates string values are exactly “ASC” or “DESC”
- Clear error messages for invalid values
Severity: N/A (resolved)
8.2 SystemStatusEnum Comparison Assumption
File: stash_graphql_client/client/mixins/system_query.py lines 80, 87
Problem:
Impact: If GraphQL returns string “NEEDS_MIGRATION” instead of enum, comparison might fail silently.
Recommended Fix: Ensure status.status field is properly typed as enum in Pydantic model, not string.
Severity: Low (Pydantic should handle enum conversion)
9. List/Tuple Element Validation (RESOLVED)
Status: ✅ RESOLVED - Timestamp type with Pydantic validators
File: stash_graphql_client/client/mixins/scene.py lines 774, 815, 981, 1022
Problem (Historical):
Issue: What format are the strings? Timestamps? ISO8601? No validation or documentation.
Resolution (scene.py:802-805):
Timestamp Type (scalars.py:121-125):
Benefits:
- Pydantic validates timestamp format
- Supports RFC3339 strings
- Supports relative times (e.g., “<4h” for 4 hours ago)
- Type-safe with datetime objects
Severity: N/A (resolved)
10. Image Index Range Validation (RESOLVED)
Status: ✅ RESOLVED - Non-negative validation added
File: stash_graphql_client/client/mixins/gallery.py lines 256, 309
Problem (Historical):
Impact: Negative or out-of-bounds indices might cause GraphQL errors.
Resolution (gallery.py:269-270):
Benefits:
- Validates non-negative indices
- Clear error messages
- Fails early before GraphQL submission
Severity: N/A (resolved)
11. verify_ssl Redundant Bool Wrapping (Fixed in v0.10.8)
Status: ✅ RESOLVED - String coercion added with validation
File: stash_graphql_client/client/base.py line 128-134
Problem (Historical): bool(verify_ssl) wrapper was redundant and couldn’t handle string values correctly.
Resolution (v0.10.8): Implemented Option 2 - validate and coerce string values:
Benefits:
- Accepts bool values:
True, False
- Accepts truthy strings:
"true", "1", "yes" → True
- Accepts falsy strings:
"false", "0", "no", etc. → False
- Rejects invalid types with clear TypeError
Severity: N/A (resolved)
Recommendations
- ✅ Fix batch_size validation (ValueError prevention)
- ✅ Fix port type coercion (URL construction safety)
- ✅ Standardize bool return patterns (consistency + KeyError prevention)
Short-Term (Next Release) ✅ COMPLETED
- ✅ Add dict structure validation for
SomeType | dict[str, Any] parameters (v0.10.8)
- ✅ Fix Path/String confusion with better validation and error messages (v0.10.8)
- ✅ Validate config dict value types at initialization (v0.10.8)
Medium-Term (Future Releases) ✅ COMPLETED
- ✅ Replace “direction: str” with actual Enum types (completed)
- ✅ Add validation for list element formats where critical (Timestamp type)
- ✅ Convert CIMultiDict to plain dict for type safety (v0.10.8)
Long-Term (Technical Debt Cleanup) - ONGOING
- Consider using TypedDict for structured dict parameters
- Add mypy strict mode checks for parameter types
- Document expected types more explicitly in all docstrings
- Add pre-commit hooks to validate parameter types
Testing Strategy
For each fix:
- Add unit test with valid input (should pass)
- Add unit test with invalid input (should raise specific error)
- Add integration test if behavior affects GraphQL layer
- Document expected error messages in test names
Example:
- [v0.10.7] Fixed TTL type handling (int → timedelta conversion)
- [v0.10.7] Fixed flaky integration tests (dynamic assertions)
- [v0.10.8] Added dict validation and config hardening
- [v0.10.9] Documentation updates
- [v0.10.10] Fixed dict.get() None-handling (25 instances)
[Future] Address remaining 18 type validation issues ALL RESOLVED
Original Creation: 2026-01-11
Last Updated: 2026-01-11
Archived: 2026-01-20
Status: ✅ All issues resolved