Production error analysis — 4 September 2026
Julien Béranger
+ Claude Opus 5
Source: /home/ubuntu/.pm2/logs/qgen-error.log (last 500 lines).
Summary
Five requests against a qgen context failed end to end (15:05, 15:07,
15:08, 15:14 and 21:46 UTC), each ending in an unhandled
TypeError: Cannot read properties of undefined (reading 'substring').
A single transient Mistral rate limit was enough to make every model in the
fallback sequence fail, and the crash then hid the failure from the caller.
The cascade
-
Rate limit on the RAG selection call.
RagService.selectRelevantFilescallsmistral-small-latestto pick which context files are relevant. Mistral answered429 {"message":"Rate limit exceeded","code":"1300"}.ERROR [MistralService] Mistral API error response: {"object":"error","message":"Rate limit exceeded",...} ERROR [RagService] Error in file selection for context qgen: Failed to process message with Mistral AI (mistral-small-latest) -
The fallback loaded the entire context. On selection failure the service returned every file in the index. The context directory is ~4.8 MB, so the system prompt became roughly 2 million tokens — larger than any configured model's context window. What was meant as graceful degradation was in fact a guaranteed failure.
-
Every model in the fallback sequence rejected the prompt, each for its own reason, so retrying across providers could not help:
Model Error anthropicprompt is too long: 2250703 tokens > 200000 maximummistral403 tier_not_allowed— "This model is not available in your subscription tier"openai429 rate_limit_exceeded— TPM limit 200 000, requested 1 205 848deepseekmaximum context length is 1048576 tokens, requested 2008311ERROR [AppService] All models in fallback sequence failed. Last error: Failed to process message with DeepSeek -
The failure crashed the logger instead of being reported. With no model succeeding,
outputstayedundefined;JSON.stringify(undefined)returnsundefined, and the log-append step called.substring(0, 10)on it:ERROR [AppService] TypeError: Cannot read properties of undefined (reading 'substring') at AppService.ask (/home/ubuntu/qgen/dist/app.service.js:542:35)The outer
try/catchinask()then swallowed the exception and returned HTTP 200 withoutput: undefined— the client saw a successful-looking empty response rather than an error.
Root causes
- Unbounded RAG fallback. Falling back to "all files" is only viable for small contexts. At 4.8 MB it turned a recoverable 429 into a total outage for that request.
- No retry on the selection call. A single 429 on a cheap, fast call took down the whole request path; one retry with backoff would have avoided all five incidents.
- Failure treated as success.
ask()had no error path for "no model produced output", so the fault surfaced as a crash in unrelated logging code and then as a silent 200.
Fixes applied
src/rag/rag.service.ts— the selection-failure fallback now returnsRAG_REQUIRED_FILESplus at mostRAG_MAX_FILESother files (and logs the degradation), instead of the whole context.src/app.service.ts— when no model succeeds,ask()throwsServiceUnavailableExceptioncarrying the last provider error;HttpExceptions are re-thrown from the outer catch instead of being converted into a 200. The log-append call is also guarded against a non-string output.src/app.service.spec.ts— the test asserting the old "return an empty response" contract now asserts the exception.
Still open
- No retry/backoff on the
mistral-small-latestselection call. mistralis unusable in the fallback sequence — the configured large model returns403 tier_not_allowedon the current subscription, so that slot always fails.docs/FALLBACKS.mdis now stale: it still documents "system loads ALL files from the context" as the RAG selection fallback.
Other errors visible in the same log
OFF_TOPIClogged as ERROR (~50 occurrences, April–September). This is normal control flow — the off-topic guardrail firing — logged at error level. Pure noise; belongs atwarnordebug.Cannot find module '/home/ubuntu/qgen/dist/main'(repeated, earliest entries). PM2 restart-looping against a missing build — the service was started beforepnpm buildhad produceddist/.- Anthropic
invalid_request_error: "Your credit balance is too low" (7 July, ~12 requests). Billing exhaustion, since resolved; note that requests did not fall through to another provider cleanly at the time.