rohitg00/agentmemory · error · Error
Refusing to read image outside managed store: ${data.raw.ima
Error message
Refusing to read image outside managed store: ${data.raw.imageData} What it means
The compress function can accept either inline base64 image data or a path to an image previously stored in agentmemory's managed store. For security, arbitrary filesystem paths are refused unless isManagedImagePath() confirms the path lives inside the managed store. This prevents the function from being used to read arbitrary files and encode them as base64.
Source
Thrown at src/functions/compress.ts:91
sdk.registerFunction("mem::compress",
async (data: {
observationId: string;
sessionId: string;
raw: RawObservation;
}) => {
const startMs = Date.now();
let imageDescription: string | undefined;
const hasImage = data.raw.modality === "image" || data.raw.modality === "mixed";
if (hasImage && data.raw.imageData && provider.describeImage) {
try {
let base64Data = data.raw.imageData;
let mimeType = "image/png";
if (!data.raw.imageData.startsWith("/9j/") && !data.raw.imageData.startsWith("iVBOR")) {
if (!isManagedImagePath(data.raw.imageData)) {
throw new Error(`Refusing to read image outside managed store: ${data.raw.imageData}`);
}
const fileBuffer = readFileSync(data.raw.imageData);
base64Data = fileBuffer.toString("base64");
if (data.raw.imageData.endsWith(".jpg") || data.raw.imageData.endsWith(".jpeg")) mimeType = "image/jpeg";
else if (data.raw.imageData.endsWith(".webp")) mimeType = "image/webp";
else if (data.raw.imageData.endsWith(".gif")) mimeType = "image/gif";
}
imageDescription = await provider.describeImage(base64Data, mimeType, VISION_DESCRIPTION_PROMPT);
logger.info("Image described by vision model", { obsId: data.observationId });
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
logger.warn("Vision model call failed, falling back to text-only compression", {
obsId: data.observationId,
error: msg,
});
}
}View on GitHub (pinned to e04ba88819)
Solutions
- Read the file yourself and pass its base64 content in raw.imageData instead of a path.
- Store the image through the managed image write path first, then pass the returned managed path.
- Verify the path is inside the configured managed store directory (check isManagedImagePath logic / data dir).
- Ensure you're on a consistent agentmemory version so the managed store location matches.
Example fix
// before
await trigger({ function_id: 'mem::compress', payload: { raw: { imageData: '/tmp/shot.png' } } });
// after
const b64 = readFileSync('/tmp/shot.png').toString('base64');
await trigger({ function_id: 'mem::compress', payload: { raw: { imageData: b64 } } }); Defensive patterns
Strategy: validation
Validate before calling
function isInlineBase64Image(s: string): boolean {
return s.startsWith('/9j/') || s.startsWith('iVBOR');
}
function toPayload(raw: string) {
if (isInlineBase64Image(raw)) return { imageData: raw };
const b64 = readFileSync(resolveManagedPath(raw)).toString('base64');
return { imageData: b64 };
} Type guard
function isInlineImageData(v: unknown): v is string {
return typeof v === 'string' && (v.startsWith('/9j/') || v.startsWith('iVBOR'));
} Try / catch
try {
return await trigger({ function_id: 'mem::compress', payload: { raw } });
} catch (e) {
if (String(e.message).includes('Refusing to read image outside managed store')) {
return await compressViaBase64(raw);
}
throw e;
} Prevention
- Default to passing base64 content, never filesystem paths.
- Only use paths returned by agentmemory's managed image write API.
- Document that ad-hoc absolute paths are blocked by design.
- Keep the daemon and its data directory stationary so managed paths stay valid.
When it happens
Trigger: Calling mem::compress with raw.imageData set to a filesystem path (e.g. /home/user/photo.png, or a relative path) that is not inside the managed image store, and whose content does not begin with JPEG ('/9j/') or PNG ('iVBOR') base64 magic prefixes.
Common situations: Passing a local screenshot path instead of reading the file yourself; hardcoding an absolute path that worked before the managed-store restriction was added (version change); moving/renaming the data directory so the path is no longer recognized as managed.
Related errors
- Invalid dateFrom: ${filter.dateFrom}
- Invalid dateTo: ${filter.dateTo}
- mem::context: AGENTMEMORY_AGENT_SCOPE=isolated is set but no
- mem::search: query must be a non-empty string
- mem::search: limit must be a positive integer
AI-assisted analysis of rohitg00/agentmemory@e04ba88819 (2026-08-30).
Data as JSON: /api/errors/718e9f47e19b28bc.
Report an issue: GitHub.