LearnNewsExamplesServices
Frontmatter
id16575
titleA multi-cause ingest failure reports one code, so it reads as single-cause
stateClosed
labels
bugai
assigneesneo-opus-vega
createdAtAug 6, 2026, 1:47 AM
updatedAtAug 6, 2026, 2:32 AM
githubUrlhttps://github.com/neomjs/neo/issues/16575
authorneo-opus-vega
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 6, 2026, 2:32 AM

A multi-cause ingest failure reports one code, so it reads as single-cause

neo-opus-vega
neo-opus-vega commented on Aug 6, 2026, 1:47 AM

Problem

When a tenant-repo ingest fails, assertErrorFreeIngestionSummary (TenantRepoSyncService.mjs) retains only the first bounded KB_* code from the summary:

const sourceCode = summary.errors
    .map(item => item?.code)
    .find(code => typeof code === 'string' && BOUNDED_KB_ERROR_CODE_PATTERN.test(code));

A single ingest can fail for several independent reasons at once — an embed failure alongside a mirror/clone failure alongside per-file parse warnings. Reporting one of them makes a multi-cause failure read as single-cause: an operator fixes the code they were shown, the lane fails identically on the next sweep, and nothing in the log says why.

The error count is also lost. lastSourceErrorCode: KB_VECTOR_EMBED_FAILED is indistinguishable between "one file failed to embed" and "every file failed to embed" — a partial ingest and a total one look the same.

Observed live: the lane sat at consecutiveFailures = 3 with a single reported lastSourceErrorCode, and the log line carried no way to tell whether that was the whole story.

What must NOT change

The suppression of messages and details is deliberate and correct, documented at TenantRepoSyncService.mjs:229 and :708 — the latter explicitly "preserved as lastSourceErrorCode without copying raw stderr, URLs, or…". An ingestion error message can carry a clone URL with an embedded token, so copying messages would breach the credential contract (#11787's constraint: no secrets in logs, manifests, or graph-visible config).

This ticket does not touch that. Bounded codes are safe by constructionBOUNDED_KB_ERROR_CODE_PATTERN is /^KB_[A-Z0-9_]{1,120}$/, which cannot express a URL, a token, or stderr. That is precisely why the codes can widen while the messages stay uncopied.

Proposal

  • Retain every distinct bounded code (sourceErrorCodes), collapsing duplicates.
  • Retain the total error count (sourceErrorCount), including entries whose code was unbounded or absent.
  • Surface both in the existing per-repo ERROR log line, so nothing added is dead: source=<first> also=<rest> errors=<n>.
  • sourceErrorCode keeps its exact prior meaning (the first bounded code), so getSourceErrorCode and lastSourceErrorCode are untouched and no consumer changes.

Acceptance criteria

  • Every distinct bounded code is retained; duplicates collapse.
  • Total error count retained, counting unbounded entries.
  • Both visible in the failure log line; suffixes omitted when they would add nothing (single code, count of 1), so the single-cause line is byte-identical to today.
  • sourceErrorCode semantics unchanged — existing consumers unaffected.
  • Redaction proven on every axis: unbounded codes, messages, credentials, and stderr never project into the log or the returned result.

Out of scope

  • Copying messages or details — deliberately forbidden, see above.
  • Surfacing the new fields in the deployment-state snapshot per-repo entries. Worth doing, but that assembly path is distinct from the repoStates.push site and needs tracing first; the log line is the cheap surface that makes the retained data readable today.
  • The embed failure itself — #16566.

Related

  • #16566 — the multi-cause failure this exists to make legible.
  • #16551 — tenant sync never backs off; the failing path freezes its own counters. Adjacent reporting defect on the same lane.

Authored by @neo-opus-vega (Claude Opus 5).

tobiu referenced in commit 8ab5705 - "fix(orchestrator): a multi-cause ingest failure names every cause, not the first (#16575) (#16576)" on Aug 6, 2026, 2:32 AM
tobiu closed this issue on Aug 6, 2026, 2:32 AM