Skip to content

Tag migration batch marks failed items as processed — memories stay untagged forever and the migration modal reappears on every page load #303

Description

@junior-xj97-chan

Environment

  • opencode-mem: 2.26.0 (installed as opencode plugin, opencode-mem@latest)
  • OS: Windows 11
  • AI provider: openai-chat via OpenRouter (deepseek/deepseek-v4-flash-0731)
  • Web UI: http://localhost:4747

Summary

The "Memory Tag Migration" modal detects memories with empty tags and offers to migrate them. Running the migration reports success, but the same memories remain untagged, so the modal reappears forever.

Root cause: in handleRunTagMigrationBatch (dist/services/api-handlers.js), when the AI call fails, the item is silently skipped — no tags written, no error recorded — but migrationProgress.processed++ still runs, so the batch is reported as complete.

Failure path

In handleRunTagMigrationBatch:

if (result.success && result.data?.tags) {
    currentTags = result.data.tags;
    await db.run("UPDATE memories SET tags = ? WHERE id = ?", [...]);
}
// if result.success === false → nothing happens here
...
const vector = await embeddingService.embedWithTimeout(m.content, ...);
await tursoVectorSearch.updateVector(db, m.id, vector, tagsVector);
migrationProgress.processed++;   // ← counted as processed even though tags were never written

The catch block only logs when an exception is thrown. A { success: false } provider result (no throw) hits none of the error handling: no migrationProgress.errors.push(...), no log("Migration error for memory", ...). The loop just continues and the item counts as done.

Why the AI call fails (verified)

The configured model intermittently returns an empty tool-call payload over OpenRouter. Reproduced 1/3 times with a direct API call: choices[0].message.tool_calls[0].function.arguments === "{}". The provider then returns { success: false } (validation/model path), which the batch handler silently swallows as described above.

Auto-capture is affected by the same empty-arguments behavior (memories get persisted with tags = NULL), which is what keeps feeding new untagged memories into the migration detector.

Observed on my instance: 4 memories from the same evening all stuck with tags = NULL; migration run produced zero errors in the log and reported completion; GET /api/migration/tags/detect still returned count: 4 afterwards.

Suggested fix

  1. In the batch loop, treat result.success === false as a failure: push to migrationProgress.errors, log it, and do NOT increment processed (or track a separate failed counter).
  2. Surface failures in the UI progress modal instead of reporting success.
  3. Optionally: retry failed items, and/or make handleDetectTagMigration ignore memories that have already been attempted N times.
  4. Related: when the model returns empty tool arguments ({}), log a warning — currently this failure mode is completely silent on the auto-capture path too.

Happy to provide more logs/details if needed.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions