Skip to main content
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

  1. High Impact Issues
  2. Medium Impact Issues
  3. Low Impact Issues
  4. 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)

2.1 Performer.update_avatar() - ✅ RESOLVED

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:
  1. No validation that ID is positive
  2. .get() returns None if key missing, then int(None) raises TypeError
  3. 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

Immediate Actions (Critical - Being Fixed) ✅ COMPLETED

  1. ✅ Fix batch_size validation (ValueError prevention)
  2. ✅ Fix port type coercion (URL construction safety)
  3. ✅ Standardize bool return patterns (consistency + KeyError prevention)

Short-Term (Next Release) ✅ COMPLETED

  1. ✅ Add dict structure validation for SomeType | dict[str, Any] parameters (v0.10.8)
  2. ✅ Fix Path/String confusion with better validation and error messages (v0.10.8)
  3. ✅ Validate config dict value types at initialization (v0.10.8)

Medium-Term (Future Releases) ✅ COMPLETED

  1. ✅ Replace “direction: str” with actual Enum types (completed)
  2. ✅ Add validation for list element formats where critical (Timestamp type)
  3. ✅ Convert CIMultiDict to plain dict for type safety (v0.10.8)

Long-Term (Technical Debt Cleanup) - ONGOING

  1. Consider using TypedDict for structured dict parameters
  2. Add mypy strict mode checks for parameter types
  3. Document expected types more explicitly in all docstrings
  4. Add pre-commit hooks to validate parameter types

Testing Strategy

For each fix:
  1. Add unit test with valid input (should pass)
  2. Add unit test with invalid input (should raise specific error)
  3. Add integration test if behavior affects GraphQL layer
  4. 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