Troubleshooting maps observable StoryMatrix failures to confirmed causes, operator actions, and tracked blocker anchors.
🚨 Live blockers
The focused contracts B1 and B13–B17 are resolved at their named boundaries. B2–B12, the flaky suite, structural work, and evidence-blocked runs remain open; this table keeps their actionable symptoms.
| ID | Observable symptom | Cause | Fix | Tracking |
|---|---|---|---|---|
| B1 | Minimal startup previously imported unavailable SFX extras | LocalSFXAdapter now defers optional imports and resource probing (src/storymatrix/infrastructure/adapters/sfx/local.py). | No workaround is required for import-safe startup; exercise local features only with their optional dependencies. | Resolved — B1 |
| B2 | NameError: name 'llm' is not defined in the legacy story crew | _create_legacy_story_crew_inline() tests and assigns llm although its parameter is llm_service (src/storymatrix/crew/crew.py). | Use the function’s injected LLM service consistently and add a regression test for the fallback. | B2 |
| B3 | Timeline generation silently falls back to deterministic output | LLMAgenticTimelineAdapter calls logger in response parsing without defining it; fallback handling absorbs the resulting exception (src/storymatrix/infrastructure/adapters/timeline/llm_agentic_timeline_adapter.py). | Define the module logger, then exercise the agentic path and assert that a valid agentic response reaches the timeline. | B3 |
| B4 | TypeError: Can't instantiate abstract class MockImageService | MockImageService implements generate_image but not the interface’s generate_image_for_scene method (src/storymatrix/infrastructure/services/image/mock_image_service.py, src/storymatrix/application/interfaces/services.py). | Implement the missing abstract operation and verify the local-only container branch. | B4 |
| B5 | BadRequestError: LLM Provider NOT provided with mock-provider/mock-response | The image-prompt crew sends the mock sentinel string to LiteLLM instead of using a mock service (src/storymatrix/crew/image_prompt_crew.py, 01_ERROR.ini). | Route mock mode to a local/mock LLM implementation or provide a valid provider/model pair without making a paid call. | B5 |
| B6 | AttributeError: 'coroutine' object has no attribute 'id' in media repository tests | FileSystemMediaAssetRepository.save is async while callers still treat it as synchronous (src/storymatrix/infrastructure/repositories/file_media_asset_repository.py). | Await repository saves at every caller and retain async boundaries through the service layer. | B6 |
| B7 | ValidationError: 2 validation errors for StoryData at golden-path setup | StoryData.background_music is required, while the golden fixture omits it (src/storymatrix/crew/schemas.py, tests/integration/test_golden_path.py). | Add the field to the fixture or establish an explicit schema default, then refresh the snapshot only after the contract is intentional. | B7 |
| B8 | AttributeError: 'str' object has no attribute 'exists' during container-wiring setup | The integration fixture supplies a string where a Path is required (tests/integration/test_container_wiring.py). | Construct a pathlib.Path in the fixture and keep path types at the container boundary. | B8 |
| B9 | Runtime annotation resolution raises NameError for sessionmaker | SQLiteContentRepository annotates sessionmaker without importing it; postponed annotations hide the defect until resolution (src/storymatrix/infrastructure/repositories/sqlite_content_repository.py). | Import sessionmaker from SQLAlchemy in the repository module and resolve annotations in a focused test. | B9 |
| B10 | Ruff reports 18 F821 errors in src/ | Undefined runtime names include the live llm, logger, and sessionmaker defects plus unresolved annotation types (src/, Ruff F821 output). | Resolve every undefined name or import, then run the targeted F821 check before broad linting. | B10 |
| B11 | Repository status shows spent scripts, raw logs, and untracked support files | Root debris includes fix_repo.py, test_reproduce.py, 01_ERROR.ini, .claude/, and scripts/pack-audit-zip.sh (git status evidence). | Classify each artifact, preserve incident evidence where needed, and remove or archive disposable debris through the repository’s normal review process. | B11 |
| B12 | Operators read a stale output-root instruction | AGENTS.md disagrees with AppSettings.output_dir, while src/storymatrix/config/models.py and storymatrix_config.yaml select out. | Follow the code contract out/ and correct the stale instruction in the owning documentation. | B12 |
| B13 | Web startup previously lacked typed title/theme/reload settings | WebSettings now declares the fields consumed by ui.run(...) (src/storymatrix/config/models.py, src/storymatrix/interfaces/web/main.py). | Use the typed settings; full browser workflow remains separately scoped. | Resolved — B13 |
| B14 | API dependencies previously rejected every request | Dependencies now read config.services.api; focused health/metrics dispatch reaches handlers (src/storymatrix/interfaces/api/main.py). | Use the repaired API contract; broader generation remains separately scoped. | Resolved — B14 |
| B15 | CLI mode flags previously lost to YAML | Typed CLI overrides now apply after YAML and environment values (src/storymatrix/cli/main.py, src/storymatrix/config/config.py). | Use explicit --offline/--low-resource; focused cost-safety evidence is complete. | Resolved — B15 |
| B16 | Loader previously printed provider strategy to stdout | The unconditional print was removed (src/storymatrix/config/config.py). | No action; unrelated suite/lint noise remains open. | Resolved — B16 |
| B17 | Status route previously required story_id and returned the wrong model field | Handler now accepts job_id only and returns StoryStatusResponse(job_id, status, result) (src/storymatrix/interfaces/api/main.py). | Poll by job ID; focused pending/ready states are schema-valid without a broker. | Resolved — B17 |
🧯 Historical incidents and resolved failures
The records below come from the raw error log and the incident archive; they are separate from the live blocker list.
| State | Symptom | Root cause | Operator action / fix | Evidence |
|---|---|---|---|---|
| Live | Image-prompt generation emits BadRequestError: LLM Provider NOT provided for mock-provider/mock-response. | LiteLLM receives a sentinel model string without a provider. | Apply B5’s mock routing fix; do not interpret deterministic output as a successful image-prompt crew run. | 01_ERROR.ini, src/storymatrix/crew/image_prompt_crew.py |
| Resolved | Montage input serialization rejects NarrationSegment as not JSON serializable. | Montage serialization passes a Pydantic segment object directly to JSON. | ffmpeg_montage.py now calls model_dump(mode="json"); treat any recurrence as regression evidence. | 01_ERROR.ini, src/storymatrix/infrastructure/services/ffmpeg_montage.py |
| Resolved | Generation appears frozen while a parent process remains visible. | A child process crashes and the parent shell masks the failure; an exception handler also references an undefined start_time. | Inspect child logs and process trees; the 2025-09-30 remediation adds four-layer timeout prevention and heartbeat logging. | WALL-OF-FAME.md (2025-09-30 05:30 UTC+2) |
| Resolved | Zombie-process recovery still lacks operator visibility. | Stage timing and heartbeat variables use incorrect scope (start_time and current_time), and audio production omits a return. | Use the heartbeat cadence and corrected variable scope from the 2025-09-30 05:55 UTC+2 remediation. | WALL-OF-FAME.md (2025-09-30 05:55 UTC+2) |
| Resolved | SFX orchestration cancels unpredictably and local-only runs lack dependable timeout behavior. | Cancellation handling and timeout paths lack explicit orchestration controls. | Use the hardened cancellation and timeout paths documented by the 2025-08-27 remediation; inspect provider logs when a request stops. | WALL-OF-FAME.md (2025-08-27) |
| Resolved | Audio montage loses SFX or music through enum serialization and FFmpeg path handling. | Audio segment values and filesystem paths do not reach montage inputs in the expected serialized form. | Keep enum serialization and Path handling covered by montage tests; the 2025-07-29 and 2025-09-12 remediations record the fixes. | WALL-OF-FAME.md (2025-07-29, 2025-09-12) |
| Resolved | name 'generate_character_id' is not defined, 100% story-generation failure, and the pipeline stops at stage 2/3 (2025-10-12 02:28–03:53 UTC+2). | A refactor extracts the character-ID utility but omits imports in audio production, character mapping, and crew transformer services. | Add the three missing imports; the incident record reports 11/11 integration tests and restored generation after a 38-minute response. | WALL-OF-FAME.md (2025-10-12 02:28–03:53 UTC+2) |
| Environmental | Local generation fails because a provider dependency or model is unavailable. | The selected install profile does not contain the local ML dependency, or the local asset/model path is absent. | Choose uv sync --extra local, verify configured asset paths, and set APP__DEV_LOCAL_ONLY=true for a no-cost path. | pyproject.toml, src/storymatrix/config/models.py |
The archive also records a persistent story-data validation failure during the 2025 stabilization work; the current schema and fixtures remain covered by B7 rather than treating the old prose as a current pass claim.
🧩 Domain exceptions
The domain exception hierarchy identifies the operator’s response class (src/storymatrix/domain/exceptions.py).
| Exception | Raised when | Operator response |
|---|---|---|
ConfigurationError | Configuration is invalid or missing. | Inspect effective settings, environment names, YAML structure, and required credentials without publishing secret values. |
EntityNotFoundError | A repository lookup cannot find the requested entity ID and type. | Confirm the ID, database selection, and preceding create/commit operation. |
DomainError | Domain logic rejects an invariant or operation. | Preserve the message and details, then correct the invalid domain input or state transition. |
RepositoryError | Repository work fails; the exception records an optional operation and details. | Check the database or filesystem backend, operation name, and path permissions, then retry only after the cause is clear. |
ValidationError | Input data fails validation checks. | Read the field-level validation message and correct the request, fixture, or schema contract. |
StoryGenerationError | Story production fails at the generation boundary. | Inspect stage logs and checkpoint state before deciding whether to resume or restart. |
LLMGenerationError | An LLM interaction fails. | Check provider selection, model configuration, request payload, and local-only routing. |
LLMTimeoutError | An LLM request exceeds its timeout. | Confirm endpoint reachability and timeout settings, then use a local or fallback provider. |
LLMInvalidRequestError | An LLM provider rejects the request as invalid. | Inspect model name and request shape; correct configuration before retrying. |
LLMRateLimitError | An LLM provider reports a rate limit. | Stop repeated retries, reduce concurrency, and wait for the provider window or switch to local mode. |
LLMServiceUnavailableError | An LLM provider is unavailable. | Check provider status and network policy, then select a local/mock route when cost control applies. |
ExternalServiceError | An external service call fails; the exception records service name and optional details. | Identify the named service, inspect credentials and endpoint configuration, and use a configured fallback. |
🧭 Escalation sequence
- Capture the exact symptom, stage, provider, and request mode from the console and
app_debug.log. - Check the matching blocker anchor before changing configuration.
- Preserve
checkpoint.jsonand artifact metadata when a run fails; resume only after the cause is understood. - Set
APP__DEV_LOCAL_ONLY=truebefore exploratory retries that must not call paid providers. - Record a new reproducible symptom with its source path and date; do not convert an unverified historical claim into a current status.