Mintplex-Labs/anything-llm · warning

Not found

Error message

Not found

What it means

Returned (HTTP 404) by DELETE /api/memories/:index when the parsed index is < 0 or >= entries.length — there is no entry at that position in MEMORY.md. Indexes are positions in the entries array read at request time, so any concurrent deletion, regeneration of the file, or stale list in the client shifts them out of range. (Edge case: a non-numeric index parses to NaN, which slips past both range comparisons and silently deletes nothing — always send a base-10 integer.)

Solutions

  1. Re-GET /api/memories immediately before each delete and use the fresh index
  2. Treat the 404 as 'already deleted' and refresh your list rather than erroring
  3. For batch deletes, delete from the highest index to the lowest so earlier splices do not shift pending indexes
  4. Always send an integer index (parseInt(idx, 10) on the client) — NaN bypasses the guard and deletes nothing

Example fix

// before: indexes computed from a stale snapshot
const {entries} = await get('/api/memories');
for (const i of selected) await del(`/api/memories/${i}`); // 404s as list shrinks

// after: refresh + descending order
for (const i of [...selected].sort((a, b) => b - a)) {
  const res = await del(`/api/memories/${i}`);
  if (res.status === 404) continue; // someone else already removed it
}
Defensive patterns

Strategy: validation

Validate before calling

const {entries} = await (await fetch(`${BASE}/api/memories`)).json();
const idx = Number.parseInt(String(rawIndex), 10);
const valid = Number.isInteger(idx) && idx >= 0 && idx < entries.length;
if (!valid) return refreshList();

Type guard

function isValidMemoryIndex(idx, entries) {
  return Number.isInteger(idx) && idx >= 0 && idx < entries.length;
}

Try / catch

const res = await del(`${BASE}/api/memories/${idx}`);
if (res.status === 404) return refreshList(); // someone else already deleted it

Prevention

When it happens

Trigger: DELETE /api/memories/7 when only 3 entries exist; re-deleting an index after another tab or the /api/review script already modified the file; computing indexes from a GET /api/memories snapshot taken before other deletions; sending '2nd' or '1.5' as the index.

Common situations: Two UIs editing memories concurrently (first delete shifts every later index); stale list after the pi review process rewrites MEMORY.md; batch delete loops that do not account for indexes shifting down after each splice.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/547ed31087703cb7. Report an issue: GitHub.

Appendix: source

Thrown at open-computer/services/memory-manager/server.js:74

});

app.post("/api/memories", (req, res) => {
  const { entry } = req.body;
  if (!entry || !entry.trim()) return res.status(400).json({ error: "Empty entry" });
  const entries = readEntries(MEMORY_FILE);
  entries.push(entry.trim());
  const combined = entries.join(` ${DELIMITER} `);
  if (combined.length > CHAR_LIMIT) {
    return res.status(400).json({ error: `Would exceed ${CHAR_LIMIT} char limit` });
  }
  writeEntries(MEMORY_FILE, entries);
  res.json({ entries });
});

app.delete("/api/memories/:index", (req, res) => {
  const entries = readEntries(MEMORY_FILE);
  const idx = parseInt(req.params.index, 10);
  if (idx < 0 || idx >= entries.length) return res.status(404).json({ error: "Not found" });
  entries.splice(idx, 1);
  writeEntries(MEMORY_FILE, entries);
  res.json({ entries });
});

app.post("/api/user", (req, res) => {
  const { entry } = req.body;
  if (!entry || !entry.trim()) return res.status(400).json({ error: "Empty entry" });
  const entries = readEntries(USER_FILE);
  entries.push(entry.trim());
  const combined = entries.join(` ${DELIMITER} `);
  if (combined.length > CHAR_LIMIT) {
    return res.status(400).json({ error: `Would exceed ${CHAR_LIMIT} char limit` });
  }
  writeEntries(USER_FILE, entries);
  res.json({ entries });
});

View on GitHub (pinned to 3aec848f28)