LearnNewsExamplesServices
Frontmatter
id16948
titleA resident Ollama model is reported missing forever, because '':latest'' is compared as a literal — and it drives a warm loop that cannot exit
stateClosed
labels
bugaiagent-os
assigneesneo-opus-grace
createdAtAug 11, 2026, 10:18 AM
updatedAtAug 11, 2026, 3:04 PM
githubUrlhttps://github.com/neomjs/neo/issues/16948
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 11, 2026, 3:04 PM

A resident Ollama model is reported missing forever, because ':latest' is compared as a literal — and it drives a warm loop that cannot exit

Closed Backlog/active-chunk-15 bugaiagent-os
neo-opus-grace
neo-opus-grace commented on Aug 11, 2026, 10:18 AM

Context

Measured on an external plane 2026-08-11 07:44Z, and reproduced in source. Their provider residency reports:

"requiredModels":  ["gemma4:26b", "qwen3-embedding"],
"availableModels": ["qwen3-embedding:latest", "gemma4:26b"],
"missingModels":   ["qwen3-embedding"],
"extraModels":     ["qwen3-embedding:latest"],
"ready": false

The embedding model is loaded. Ollama stores it as qwen3-embedding:latest; our config asks for qwen3-embedding. Ollama itself resolves an untagged name to :latest, so every embed request works — this is not a dispatch failure.

The Problem

providerReadinessHelper.mjs:1337 and :1841:

const getMissing = available => requiredModels.filter(model => !available.includes(model));

Exact string comparison, no tag normalisation. 'qwen3-embedding' !== 'qwen3-embedding:latest', so a resident model is reported missing permanently — the condition cannot clear, because warming the model produces the same :latest id that already failed to match.

That false negative is not inert. It drives an actuator:

missingModels.length > 0
  → getProviderResidencyReasonCode() → 'missing-required-model'          (ContainerHealthDiagnosisService:1392)
  → isProviderRoleResidencyRecoverable()                                  (:1436)
  → recoveryClass 'provider-role-residency', actionClass warmProvider,
    confidence 0.85, reason 'provider-role-residency-warm'                (:1071-1080)

A self-sustaining warm loop. Diagnose missing → warm the provider → model loads under :latest → still "missing" → diagnose again. The gate can never be satisfied, so the loop has no exit.

Why this is Ollama-specific

Ollama canonicalises to name:tag and reports :latest for an untagged pull. LM Studio ids do not gain a suffix, so the same config matches exactly there. That is consistent with the operator's observation that our own plane shows this only when run on Ollama — reproduced locally for ~2h on Monday — and never on LM Studio.

Why it looks like an ingestion problem and is not

On the observed plane, simultaneously:

model container : 395% CPU (cap is cpus:4.0 — this is a CEILING reading, not a work measure)
knowledge (KB)  : 0.1% CPU, 60.4M
ingestion       : ZERO attempts — all 4 repos backoff-suppressed, sweep "starved"
host            : 64 cores, 93% idle

Four cores of continuous provider work while the KB is idle and ingestion has never once attempted. The burn is not the embed path. #16780's framing — burning with nothing asking it to — is satisfied by this mechanism.

Not asserted: that this is the sole cause of the four-core lock. It is a mechanism that (a) exists in source, (b) cannot self-clear, (c) drives repeated provider work, and (d) is present on the observed plane right now. The discriminator is whether warm actions appear in their heal ledger at cadence.

The Fix

Normalise the tag on both sides of the comparison before matching — name and name:latest are the same model to Ollama, and must be to us. Both getMissing sites, plus extraModels (which currently reports the same model as extra while calling it missing, in one payload).

Do not fix by requiring tags in config: NEO_OLLAMA_EMBEDDING_MODEL=qwen3-embedding is valid Ollama and every existing deployment uses it.

Acceptance Criteria

  • AC-1 — a required model X matches an available X:latest; missingModels is empty and ready is true. Fails against the current exact-match.
  • AC-2 — NON-VACUITY: a genuinely absent model is still reported missing, and ready is false. The fix must not make the check unfalsifiable.
  • AC-3 — a model cannot appear in missingModels and extraModels in the same payload; that pair is self-contradictory and is what made the report unreadable.
  • AC-4 — an explicit tag in config (qwen3-embedding:8b) still requires that exact tag; :8b must NOT match :latest. Only the untagged↔:latest equivalence is added.
  • AC-5 — a diagnosis fact for missing-required-model is not emitted when the model is resident under its canonical tag, so warmProvider is not scheduled against a satisfied requirement.

Avoided Traps

  • Do not normalise by stripping all tags. qwen3-embedding:8b and qwen3-embedding:4b are different models with different vector dimensions; collapsing them would silently accept the wrong embedder and corrupt a corpus. Only untagged ↔ :latest.
  • Do not treat this as a reporting-only fix. The false negative reaches an actuator; a cosmetic repair to the warning string leaves the loop running.
tobiu referenced in commit 874e6ab - "fix(ai): a resident Ollama model was reported missing forever, and it drove a warm loop (#16948) (#16950) on Aug 11, 2026, 3:04 PM
tobiu closed this issue on Aug 11, 2026, 3:04 PM