The gap
TextEmbeddingService.#postOpenAiCompatible's retry guard (isModelLoadError, ai/services/memory-core/TextEmbeddingService.mjs:511) matches only HTTP-400 model-load shapes:
- Shape A:
HTTP 400 + Model was unloaded (JIT-unload-then-queued request)
- Shape B:
HTTP 400 + Failed to load model + Operation canceled (JIT-warm-load canceled)
An HTTP-404 (...HTTP 404: The requested resource could not be found... [endpoint=…, model='…'] — the model not resident at the provider) does not match, so it skips the unloadRetriesLeft model-load-wait path and exhausts the remaining blind POSTs, then aborts the batch.
Why it matters
This is the live #14154 trigger: a ~10-minute window where LM Studio had evicted the embedding model returned HTTP-404 across all retries, so the kbSync batch aborted (and, pre-#14146, discarded all progress). The model-load-wait retry — which waits unloadRetryDelayMs between attempts, giving the provider time to reload — is exactly the right response to a model-not-resident 404, but the guard doesn't recognize it. This is the Neo-side self-heal-not-abort mitigation (the env-root-cause — VRAM co-eviction — is #14154's separate infra investigation). Identified as sub-fix #3 in #14146 (closed; the checkpoint/resume half landed, this retry-guard half did not).
The fix
Add Shape C to isModelLoadError: err.message.includes('HTTP 404') (a 404 on /v1/embeddings = model not resident; the error body already carries the endpoint + model for diagnosability). A 404 then triggers the existing bounded model-load-wait retry, and stays fail-loud on a genuinely-permanent 404 (the bounded unloadRetriesLeft exhaust and the batch fails with the diagnostic message — no infinite loop). Update the Shape A/B/C log discriminator.
Acceptance
Scope
TextEmbeddingService.#postOpenAiCompatible retry-guard only — internal retry logic, no consumed public surface (no Contract Ledger). Refs #14154 (incident + env-root-cause), #14146 (sub-fix #3 origin), #14124 (warm-provider / embed-write-canary class).
Authored by Ada (@neo-opus-ada · Claude Opus 4.8, Claude Code).
The gap
TextEmbeddingService.#postOpenAiCompatible's retry guard (isModelLoadError,ai/services/memory-core/TextEmbeddingService.mjs:511) matches only HTTP-400 model-load shapes:HTTP 400+Model was unloaded(JIT-unload-then-queued request)HTTP 400+Failed to load model+Operation canceled(JIT-warm-load canceled)An HTTP-404 (
...HTTP 404: The requested resource could not be found... [endpoint=…, model='…']— the model not resident at the provider) does not match, so it skips theunloadRetriesLeftmodel-load-wait path and exhausts the remaining blind POSTs, then aborts the batch.Why it matters
This is the live #14154 trigger: a ~10-minute window where LM Studio had evicted the embedding model returned HTTP-404 across all retries, so the kbSync batch aborted (and, pre-#14146, discarded all progress). The model-load-wait retry — which waits
unloadRetryDelayMsbetween attempts, giving the provider time to reload — is exactly the right response to a model-not-resident 404, but the guard doesn't recognize it. This is the Neo-side self-heal-not-abort mitigation (the env-root-cause — VRAM co-eviction — is #14154's separate infra investigation). Identified as sub-fix #3 in #14146 (closed; the checkpoint/resume half landed, this retry-guard half did not).The fix
Add Shape C to
isModelLoadError:err.message.includes('HTTP 404')(a 404 on/v1/embeddings= model not resident; the error body already carries the endpoint + model for diagnosability). A 404 then triggers the existing bounded model-load-wait retry, and stays fail-loud on a genuinely-permanent 404 (the boundedunloadRetriesLeftexhaust and the batch fails with the diagnostic message — no infinite loop). Update the Shape A/B/C log discriminator.Acceptance
unloadRetriesLeft.Scope
TextEmbeddingService.#postOpenAiCompatibleretry-guard only — internal retry logic, no consumed public surface (no Contract Ledger). Refs #14154 (incident + env-root-cause), #14146 (sub-fix #3 origin), #14124 (warm-provider / embed-write-canary class).Authored by Ada (@neo-opus-ada · Claude Opus 4.8, Claude Code).