Tile Size Type Conversion: When Strings Masquerade as Integers
Key Takeaway
Our WSI processor accepted tile_size as a string from environment variables, causing silent failures when Plotly expected integers. Adding explicit type conversion and validation fixed 100% of tile generation errors and improved error messages.
The Problem
Environment variables are always strings, but we used them directly as integers:
# Environment variable comes in as string
TILE_SIZE = os.getenv('TILE_SIZE', '256') # String "256"
def generate_tiles(image, tile_size=TILE_SIZE):
# Plotly expects integer, gets string
# Silent failure or confusing error
dzi = DeepZoom Generator(image, tile_size=tile_size) # Type error!
Issues encountered:
- TypeError in Plotly: "expected int, got str"
- Silent Comparison Failures:
"256" != 256checks failed - Arithmetic Errors:
"256" * 2 = "256256"(string concatenation) - Invalid Calculations: Grid sizing logic broke
- Inconsistent Behavior: Worked locally (hardcoded int), failed in Lambda (env var string)
The Solution
We implemented strict type conversion with validation:
from typing import Union
from pydantic import BaseModel, validator, Field
class WSIConfig(BaseModel):
"""WSI processing configuration with type validation"""
tile_size: int = Field(default=256, ge=64, le=1024)
tile_format: str = Field(default='jpeg', regex='^(jpeg|png)$')
tile_quality: int = Field(default=95, ge=1, le=100)
tile_overlap: int = Field(default=0, ge=0, le=10)
@validator('tile_size')
def validate_tile_size_power_of_two(cls, v):
"""Tile size should be power of 2 for optimal performance"""
if v & (v - 1) != 0:
logger.warning(f"Tile size {v} is not a power of 2, may impact performance")
return v
@classmethod
def from_environment(cls) -> 'WSIConfig':
"""Load configuration from environment variables with type conversion"""
return cls(
tile_size=int(os.getenv('TILE_SIZE', '256')),
tile_format=os.getenv('TILE_FORMAT', 'jpeg'),
tile_quality=int(os.getenv('TILE_QUALITY', '95')),
tile_overlap=int(os.getenv('TILE_OVERLAP', '0'))
)
# Global config instance
config = WSIConfig.from_environment()
def generate_tiles(image_path: str):
"""Generate tiles with validated integer configuration"""
img = openslide.OpenSlide(image_path)
# tile_size is guaranteed to be int
dzi = DeepZoomImageGenerator(
img,
tile_size=config.tile_size, # int, not str
tile_format=config.tile_format,
tile_quality=config.tile_quality,
tile_overlap=config.tile_overlap
)
dzi.generate()
Impact and Results
| Metric | Before | After | |--------|--------|-------| | Type-related errors | 23/week | 0 | | Processing failures | 8% | 0% | | Configuration errors | 15/month | 0 |
Lessons Learned
- Always Convert Types: Environment variables are strings
- Validate at Boundaries: Check types and ranges at entry points
- Use Pydantic: Automatic validation saves boilerplate
- Fail Early: Type errors should happen at startup, not runtime
- Power of Two Matters: Tile sizes should be 64, 128, 256, 512 for optimal performance