mastra-ai/mastra · error · HTTPException

Invalid plan path

Error message

Invalid plan path

What it means

After confirming the agent has the plan tool, the plans handler validates the requested file path with `isPlanPath(path)`; paths that don't match the allowed plan-file convention throw a 400 'Invalid plan path'. This guards reads/writes to only designated plan files within the workspace.

Source

Thrown at packages/server/src/server/handlers/plans.ts:47

  description:
    'Returns a markdown plan when the agent exposes the core submit_plan capability and the path is under .mastracode/plans/.',
  tags: ['Agents', 'Tools'],
  requiresAuth: true,
  requiresPermission: MastraFGAPermissions.AGENTS_READ,
  handler: async ({ agentId, mastra, path, requestContext, status, versionId }) => {
    try {
      const versionOptions = versionId ? { versionId } : status ? { status } : undefined;
      const agent = await getAgentFromSystem({ mastra, agentId, versionOptions, requestContext });
      const tools = await agent.listTools({ requestContext });
      const hasSubmitPlan = Object.values(tools).some(
        tool => typeof tool === 'object' && tool !== null && 'id' in tool && tool.id === submitPlanTool.id,
      );

      if (!hasSubmitPlan) {
        throw new HTTPException(404, { message: 'Plan capability not found' });
      }
      if (!isPlanPath(path)) {
        throw new HTTPException(400, { message: 'Invalid plan path' });
      }

      const workspace = await agent.getWorkspace({ requestContext });
      const filesystem = await workspace?.resolveFilesystem({ requestContext });
      if (!filesystem) {
        throw new HTTPException(404, { message: 'No workspace filesystem configured' });
      }
      if (!(await filesystem.exists(path))) {
        throw new HTTPException(404, { message: `Plan file "${path}" not found` });
      }

      const content = await filesystem.readFile(path, { encoding: 'utf-8' });
      return {
        path,
        content: typeof content === 'string' ? content : content.toString('utf-8'),
      };
    } catch (error) {
      return handleError(error, 'Error reading submitted plan');

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Use the exact plan path convention the handler expects (the isPlanPath-accepted pattern, e.g. the designated plan file name).
  2. Log/inspect the path being sent and compare it against isPlanPath's pattern.
  3. Normalize path separators to forward slashes and avoid absolute paths.
  4. If you need arbitrary files, use the workspace file APIs instead of plan endpoints.

Example fix

// before
await getPlan('agent-1', '/tmp/other-plan.md'); // rejected
// after
await getPlan('agent-1', 'plan.md'); // matches isPlanPath convention
Defensive patterns

Strategy: validation

Validate before calling

const PLAN_PATH = /^plan\.md$/; // match isPlanPath convention
if (!PLAN_PATH.test(path)) throw new Error(`'${path}' is not a valid plan path`);

Type guard

function isPlanFilePath(path: string): path is 'plan.md' {
  return path === 'plan.md';
}

Try / catch

try {
  return await getPlan(agentId, path);
} catch (e) {
  if (e.status === 400 && e.message === 'Invalid plan path') {
    console.error(`Path '${path}' violates the plan path convention`);
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling a plan API with a path outside the accepted plan directory/extension (e.g. 'notes.md' instead of the plan path pattern, absolute paths, path traversal attempts, or missing the required prefix like 'plan.md').

Common situations: Hardcoding a path that doesn't match the plan convention; using backslashes on Windows-style paths; trying to read arbitrary workspace files through the plan endpoint; upstream components writing a different plan filename than the handler expects.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/2a78fa784bb1dd3c. Report an issue: GitHub.