MemoryLake
Back to all articles
TutorialSeptember 9, 2026·12 min read

How to Keep OpenHands' Memory Index From Dropping Your Oldest Facts (2026 Guide)

You turned on persistent memory in OpenHands and it worked. The agent started recording what it learned — the environment quirk that took an afternoon to find, the reason a service is configured oddly, the preference you had to repeat four times. Sessions began knowing things.

Three months later the oldest entries are gone. Not deleted by you, not deleted by the agent's own pruning instructions, just no longer in the prompt. And the ones that went are the earliest ones — which, if you have been running the same project all along, are the foundational decisions rather than last week's noise.

This is documented behavior, not a bug, and once you know the shape of it the fix is a ten-minute change to how you write one file. The second half of this guide is about a different sentence in the same documentation that changes what you should put in there at all.

If you are still setting the tool up, migrating from Codex to OpenHands covers the instruction-file side of the move; this guide is about the memory feature specifically.

Why the oldest lines are the ones that go

Persistent memory in OpenHands is off unless you ask for it. The documentation is explicit: the feature is "opt-in and off by default," and without it "agents keep the existing AGENTS.md-based guidance and prompts are unchanged." You enable it by setting load_memory to true on the agent's AgentContext.

Once on, there are two tiers. A user tier at ~/.openhands/memory/ holds "knowledge and preferences that apply across all projects," and a project tier at <workspace>/.openhands/memory/ holds "knowledge specific to the current repository." Each tier contains a MEMORY.md — described as "a curated index of durable facts. This is the only file injected into the prompt" — plus dated daily logs which "are never injected automatically; the agent reads them on demand with its file tools when MEMORY.md points to them."

That split is the important architecture, and it is also where the eviction comes from:

"Size budget: the combined indexes are capped at ~6,000 characters. When the budget is exceeded, whole lines are dropped from the top of each over-budget tier (the oldest content) -- partial lines never survive, the tier headers are always kept, and a truncation notice appears under the header of any tier that lost lines. Keep indexes curated."

Read it slowly, because four separate behaviors are packed in there.

The cap is on the combined indexes across both tiers, so your personal preferences and your project facts compete for the same budget. A verbose user-tier file eats into what the project tier can carry.

Lines are dropped from the top, and the documentation glosses the top as "the oldest content." Since the agent appends as it learns, the top of the file is the earliest thing it ever recorded. Most caps evict the newest thing or fail the write. This one removes your foundations first.

The dropping is line-granular and clean — "partial lines never survive" — so you never get a half-sentence that changes meaning. Good design, and it means a single long line is all-or-nothing.

And there is a signal: "a truncation notice appears under the header of any tier that lost lines." Nothing is silent here if you look. The problem is that nobody looks at an injected system-prompt block during a normal working session.

Worth noting for anyone running multiple tools: 6,000 characters is the same budget Devin Desktop documents for its global rules file. Somewhere around six thousand characters is where several vendors independently decided an always-loaded file stops being worth its cost, which is a useful number to internalize regardless of which tool you use.

What people try instead

Raising the budget. The first instinct, and the documentation does not offer it as a knob — the cap is described as a property of the feature. Even if it were adjustable, a bigger always-loaded block is a worse trade, not a better one, for the reasons how much memory you should give an AI agent works through.

Rewriting MEMORY.md by hand every few weeks. This works and it is what "keep indexes curated" is asking for. It is also a chore with no trigger, so it happens twice and then stops, and the file grows back.

Moving everything into AGENTS.md so it is never evicted. Tempting, and it misreads the division of labor the documentation sets out. The agent is instructed to "keep AGENTS.md for instructions addressed to any agent working in the repository -- memory is for what the agent learned itself." Dumping learned history into an instruction file gives you a long always-loaded file with a different name and no eviction notice at all.

Putting the important facts in a skill so they load on demand. A reasonable instinct that runs into the wrong mechanism, for the reasons in why agent skills aren't memory. Skills answer "how do I do this task." A rejected architectural alternative is not a task.

Committing .openhands/memory/ and treating it as team documentation. The docs explicitly allow this — "a project team can even commit .openhands/memory/ to share agent-learned knowledge" — and it is genuinely useful. But it makes the eviction problem worse rather than better, because now several people's agents are appending to a shared index against the same 6,000-character ceiling.

There is a further reason none of these quite work, and it is the sentence people miss:

"Untrusted by design: the injected block is wrapped in <UNTRUSTED_CONTENT>. Memory files are typically agent-written, but anyone with access to the workspace or repository can edit or commit them (a cloned repo may ship a .openhands/memory/MEMORY.md), so the agent is told they may contain prompt injection, and to treat them as unverified hints, never as authoritative instructions."

The agent is instructed to treat its own memory as unverified hints. That is the correct security posture — a cloned repository really can ship a memory file — and it settles a design question for you. Anything that must be followed cannot live in memory, because memory is explicitly not authoritative. Memory is for context the agent may find useful. Instructions belong in AGENTS.md, where they are addressed to any agent and read as instructions.

So the two documented properties combine into one rule: the memory index is a small, non-authoritative, lossy-at-the-top pointer file. Treat it as anything more and it will disappoint you in one of two ways.

The Fix: Keep the index a pointer, not a store

Three steps. The first two cost ten minutes; the third is what stops the problem recurring.

Step 1: Turn MEMORY.md into an index of pointers

The documentation already tells you the intended shape — MEMORY.md is "a curated index of durable facts," the agent is told to "put long detail in daily logs," and the logs are read "on demand with its file tools when MEMORY.md points to them."

So every line in the index should be short and should point somewhere. One line per fact, phrased so the agent knows both what is true and where the detail is. Prose paragraphs, code samples, and long explanations move into the dated log files, where they are read only when needed and cost nothing against the budget.

Do this and the 6,000-character cap stops being a constraint. A hundred one-line pointers fit comfortably; a dozen paragraphs do not.

Step 2: Reorder the index so the top is disposable

Because eviction removes lines from the top and the top is the oldest content, the file's chronological order works directly against you. Fix it by making the order semantic instead.

Put the facts you would be sorry to lose at the bottom of each tier's index — the ones about architecture, constraints, and long-standing decisions. Put transient operational notes at the top. Now when the budget is exceeded, the lines that go are the ones you would have pruned anyway.

Then rebalance across tiers. Because the cap is combined, a bloated ~/.openhands/memory/MEMORY.md full of personal preferences is directly taking space from project facts. Keep the user tier to genuine cross-project preferences and let the project tier have the room.

While you are in there, check for the truncation notice under each tier header. If it is present, you have already lost lines, and the daily logs are where you will find what they said — which is a good argument for doing Step 1 first.

Step 3: Separate the three things currently sharing one file

The index is now holding three kinds of content that want different homes.

Instructions — things that must be followed — belong in AGENTS.md, per the documentation's own division. They are not memory, and memory is not authoritative.

Agent-learned operational detail — the environment quirk, the flaky test, the command that actually works — belongs exactly where it is: a pointer line in the index and the detail in a daily log. This is what the feature is for.

Project decisions and their reasons — why the queue library was rejected, what the compliance constraint actually requires, what was tried in March and did not work — belong in neither. They are not instructions, so AGENTS.md is wrong. They must not be lossy or non-authoritative, so the memory index is wrong. And they need to be answerable by every tool your team uses, not just the one with load_memory switched on.

Setting this up in MemoryLake

MemoryLake is the home for that third category. It holds decisions and their reasons outside any single agent, with no always-loaded budget to compete for, and answers questions about them over MCP or the API. Your MEMORY.md stays a short pointer index and OpenHands keeps maintaining it exactly as documented; the durable reasoning lives somewhere a character cap cannot reach.

Step 1: Create an API key

Generate a key and make your first request in about thirty seconds. Do this before Step 2 above, so you have somewhere to move each decision as you reorder the index.

Creating a MemoryLake API key so the facts that outgrow MEMORY.md have somewhere durable to live
Creating a MemoryLake API key so the facts that outgrow MEMORY.md have somewhere durable to live

Step 2: Upload your first memories

Read through the current index and the daily logs it points at. Every entry that is a decision rather than an observation gets written down with what was chosen, what was rejected, and why. Supporting documents and files go in the same place.

Uploading the founding project decisions into MemoryLake instead of holding them in a capped index
Uploading the founding project decisions into MemoryLake instead of holding them in a capped index

Step 3: Connect your AI & agents

Give OpenHands, Claude, Codex, and your other agents access over MCP or the API. When any of them needs to know why the project is the way it is, the answer arrives with its reasoning attached instead of competing for space in a system prompt.

Connecting OpenHands and other agents to MemoryLake over MCP and the API
Connecting OpenHands and other agents to MemoryLake over MCP and the API

What this changes in practice

The first change is that eviction stops mattering. A pointer index ordered with the disposable content at the top loses only what you would have pruned, and the detail behind every pointer is still in a log file the agent can open.

The second is that the memory feature gets to be good at its actual job. Recording that the integration tests need a specific environment variable is exactly what an agent-maintained store should do, and it does that well when it is not also being asked to be the team's decision record.

The third is that the untrusted-content framing stops being a problem. Once nothing load-bearing is in memory, the fact that the agent treats it as unverified hints is simply correct — and the things that do need to be authoritative are in AGENTS.md, where they are read as instructions.

The fourth is that committing .openhands/memory/ becomes safe to do at team scale. Several agents appending pointer lines against a 6,000-character budget is sustainable in a way that several agents appending paragraphs is not. And when two entries genuinely disagree, you have somewhere to resolve it rather than letting alphabetical or chronological accident decide — the problem memory conflict detection exists to catch.

Best practices for OpenHands persistent memory

One line, one fact, one pointer. The index is documented as a curated index. Anything longer than a line belongs in a daily log with the index pointing at it.

Order by importance, not by time. Eviction takes lines from the top. Put what you can afford to lose there.

Budget the two tiers against each other. The cap is combined, so a verbose user tier silently shrinks your project tier.

Check for the truncation notice. It appears under the header of any tier that lost lines. It is the only signal you get, and it is reliable if you look.

Never put credentials in memory. The agent's own instructions say to never record secrets or credentials. Do not undercut that by adding them yourself, and audit periodically — auditing what your AI remembers covers the habit.

Skip anything trivially re-discoverable. The maintenance instructions say as much: directory listings and obvious commands cost budget and teach nothing.

Keep instructions out of memory entirely. Memory is documented as unverified hints. If it must be obeyed, it goes in AGENTS.md.

Remember memory is re-read, not stored in the session. The docs note the resolved text is excluded from conversation persistence and API payloads and re-read from disk each session, so editing the file by hand takes effect on the next conversation.

Conclusion

OpenHands documents its persistent memory precisely: opt-in and off by default, two tiers, only MEMORY.md injected, a combined cap of about 6,000 characters, whole lines dropped from the top of an over-budget tier with a truncation notice under the header, and the whole block wrapped in an untrusted-content marker the agent is told to treat as unverified hints rather than authoritative instructions. Every one of those is a reasonable decision. Together they describe a small, lossy, advisory pointer file — which is a genuinely useful thing and not a place to keep your project's reasoning.

Keep the index to one-line pointers, order it so the top is what you can afford to lose, and split out the three kinds of content currently sharing it. Instructions go to AGENTS.md. Observations stay in memory and its logs, which is what the feature was built for. Decisions and their reasons go somewhere with no character budget and no expiry, because those are the ones you will still need in a year — and, as keeping less in agent memory argues, a smaller always-loaded file is better on every axis once the rest has a home.

Frequently asked questions

Why did my oldest memory entries disappear?

Because that is the documented eviction order. The combined memory indexes are capped at about 6,000 characters, and when the budget is exceeded whole lines are dropped from the top of each over-budget tier, which the documentation identifies as the oldest content. A truncation notice appears under the header of any tier that lost lines.

Can I increase the memory size limit?

The documentation presents the roughly 6,000-character cap as a property of the feature rather than a configurable setting, and its guidance is to keep indexes curated. The practical way to get more into the budget is to make each line a short pointer and move detail into the daily log files, which are not injected automatically.

What is the difference between MEMORY.md and the daily logs?

MEMORY.md is the curated index and the only file injected into the prompt. The dated daily logs are free-form working notes that are never injected automatically — the agent reads them on demand with its file tools when the index points to them. That is the intended split, and it is what makes the cap manageable.

Why is the memory block marked as untrusted content?

Because memory files sit on disk in a workspace or repository that other people can write to, and a cloned repo may ship its own memory file. The documentation says the agent is told the content may contain prompt injection and to treat it as unverified hints, never as authoritative instructions. That is why anything which must be obeyed belongs in AGENTS.md instead.

Should I commit .openhands/memory/ to the repository?

The documentation explicitly allows it as a way to share agent-learned knowledge with the team, and it works. Do it only after converting the index to short pointers, though, because multiple agents appending to a shared index against a single combined budget will hit the cap much faster than one will.

Is persistent memory on by default?

No. The documentation describes it as opt-in and off by default, enabled by setting load_memory on the agent's context, and notes that without it agents keep the existing AGENTS.md-based guidance with prompts unchanged. Turning it on also switches the system prompt's memory section to instructions about maintaining the files.