Skip to content

bug: ghost trace in episode.trace_ids_json causes infinite rescore loop (590 calls, 14.5 RMB wasted) #1966

Closed

Description

@7889545

Summary

A ghost trace ID () was added to an episode's but never existed in the table. This caused to return indefinitely, triggering 590 reward rescore calls over 5 days and wasting ~14.5 RMB on LLM calls.

Root Cause

1. Ghost trace creation

The episode was reopened with . During the follow-up merge, was called which directly overwrites without validating that trace IDs actually exist in the table.

// episodes.js line 80-82
appendTrace(id, traceIds) {
    appendTrace.run({ id, trace_ids_json: toJsonText(traceIds) });
    // No validation that traceIds exist in traces table!
}

2. Reward traceCount mismatch

loads traces via which only returns existing traces:

// reward.js line 48-52
const traces = traceIds.length > 0
    ? deps.tracesRepo.getManyByIds(traceIds).sort(...)
    : [];

This returns 11 traces (ghost excluded). After scoring: reward.traceCount = 11.

3. Dirty check compares against episode.traceIds.length

// memory-core.js line 1003-1006
const traceCount = reward.traceCount;
if (typeof traceCount === number) {
    return traceCount !== (ep.traceIds?.length ?? 0);
    // 11 !== 12 → true → dirty forever!
}

4. Infinite loop

runs every 10 minutes, finds the episode dirty, rescores, gets 11 vs 12 mismatch, episode stays dirty. Repeat 590 times.

Evidence

Episode: ep_95n61b3jzycd
trace_ids_json count: 12 (including ghost tr_xhbp6c9p450r)
tr_xhbp6c9p450r in traces table: 0 (does not exist)
reward.traceCount: 11
episodeRewardIsDirty: true (traceCount mismatch)
Reward calls: 590 over 5 days (06-18 to 06-22)

Suggested Fixes

  1. appendTrace validation: Validate trace IDs exist before appending
  2. Dirty check resilience: Compare against actual existing trace count, not episode.traceIds.length
  3. Rescore retry limit: Add max retry count per episode to prevent infinite loops
  4. Ghost trace cleanup: Add startup scan to remove orphaned trace IDs from episodes

Related Issues

Environment

  • MemOS: v2.0.20
  • Agent: hermes
  • OS: Windows 11

Activity

  1. added
    types:enhancementNew feature or improvement | 新功能或改进
    area:coreMOS 编排层 / 框架底座 / 跨模块问题
    on Jun 24, 2026
  2. added
    ai:taskDispatched to AI coding agent | 已派发给 AI 编码任务
    on Jun 29, 2026
  3. Memtensor-AI commented on Jun 30, 2026

    @Memtensor-AI
    Collaborator

    🤖 AutoDev has picked up this issue and started working on it.

    Task ID: 35fc16a4a540b16a
    Working branch: feature/autodev-1966
    Target branch: latest dev* branch
    Workflow: opsp (analysis → coding → testing → PR)

    I will post the PR link here once done. If I need more information, I will ask in the comments.

  4. self-assigned this
    on Jun 30, 2026
  5. Memtensor-AI commented on Jun 30, 2026

    @Memtensor-AI
    Collaborator

    ✅ AutoDev task 35fc16a4a540b16a completed.

    Summary: Fix #1966 — the reward dirty-check infinite loop triggered by ghost trace IDs lingering in episodes.trace_ids_json (the reporter wasted 590 LLM calls / ~14.5 RMB over 5 days). The bug lives in apps/memos-local-plugin: episodeRewardIsDirty compared reward.traceCount against the raw ep.traceIds.length, so a single orphan ID kept the episode dirty forever and runDirtyClosedRewardScan re-scored it every 10 minutes.

    Three-layer fix (defence in depth): (A) episodeRewardIsDirty now compares reward.traceCount against the count of trace IDs that actually exist in the traces table, via a new traces.countExisting helper (single SELECT COUNT(*) per 900-id chunk, no BLOB hydration — same cost profile as the hasAnyNewerThan fix added for #1787). This deterministically breaks the loop. (B) repos.episodes.appendTrace now strips ghost IDs before writing trace_ids_json, so new ghosts cannot accumulate. (C) Two new traces-repo helpers — countExisting(ids) and filterExistingIds(ids) — back both fixes with the same chunked-SELECT pattern. The issue's other two suggested fixes (retry counter, startup ghost-scan) were intentionally skipped: Fix A breaks the loop without them, and adding either would slow init or introduce extra state to reason about.

    Tests: tests/unit/storage/repos.test.ts updated to match the new appendTrace contract (insert real traces first, then assert ghost-strip on mixed / all-ghost / re-append inputs) and gains coverage for both new helpers; tests/unit/pipeline/memory-core.test.ts adds an integration test "does not rescore a closed episode whose only mismatch is a ghost trace ID (#1966)" that seeds the exact reporter scenario and asserts r_task and meta.reward.rHuman stay untouched after init(). npm run lint passes clean; npx vitest run tests/unit → 1041 passed, only 2 pre-existing failures remain (migrator schema-column drift and a traces-count pagination cap, both confirmed to fail on dev-20260624-v2.0.22 via git stash and both unrelated to ghost-trace handling). No DB migrations, no schema changes, no API surface changes; all 4 existing appendTrace callers insert trace rows before referencing them, so the new validation is a no-op for valid inputs.

    Base branch: main
    Branch: feature/autodev-1966
    Commit: ed6d25f
    PR: #2004
    Assigned to: @MatthewZhuang, @CarltonXiang, @syzsunshine219, @World-controller
    Reviewers: @MatthewZhuang, @CarltonXiang, @syzsunshine219, @World-controller

  6. added
    ai:testingAI agent is running tests | AI 正在运行测试
    ai:pr-readyAI-created PR is ready for review | AI 生成的 PR 已等待评审
    and removed
    ai:codingAI agent is coding | AI 正在编码
    ai:testingAI agent is running tests | AI 正在运行测试
    ai:taskDispatched to AI coding agent | 已派发给 AI 编码任务
    on Jun 30, 2026
  7. added a commit that references this issue on Jul 2, 2026
    124ba66
  8. added and removed
    ai:pr-readyAI-created PR is ready for review | AI 生成的 PR 已等待评审
    on Jul 2, 2026
  9. added a commit that references this issue on Sep 18, 2026
    b0e4074
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:coreMOS 编排层 / 框架底座 / 跨模块问题types:enhancementNew feature or improvement | 新功能或改进

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions