affaan-m/ECC · error

createPlanCanvasServer requires a session store

Error message

createPlanCanvasServer requires a session store

What it means

createPlanCanvasServer() constructs the Plan Canvas HTTP/SSE server and requires a session store — the object that persists session, feedback, and presence state. If the store parameter is missing or falsy the factory throws immediately rather than starting a server with no persistence. It is a constructor-time guard so misconfiguration surfaces at startup, not on first request.

Solutions

  1. Create and pass a session store: createPlanCanvasServer({ store: new FileSessionStore(dir) }) (use the store implementation your setup documents).
  2. Check the call site for a typo'd or missing 'store' key in the options object.
  3. Verify the store factory/instantiation did not fail and return undefined — log its value before the call.
  4. Follow the library's documented bootstrap (e.g. the canvas command in scripts) instead of hand-rolling the factory call.

Example fix

// before
const server = createPlanCanvasServer({ port: 4111 });
// after
const store = new FileSessionStore(stateDir);
const server = createPlanCanvasServer({ port: 4111, store });
Defensive patterns

Strategy: validation

Validate before calling

if (!store) {
  throw new Error('Plan Canvas: create the session store before createPlanCanvasServer.');
}
const server = createPlanCanvasServer({ ...opts, store });

Type guard

function hasSessionStore(opts) {
  return Boolean(opts && opts.store && typeof opts.store === 'object');
}

Try / catch

try {
  const server = createPlanCanvasServer(opts);
} catch (e) {
  if (e.message === 'createPlanCanvasServer requires a session store') {
    console.error('Options object is missing `store` — construct the store (e.g. new FileSessionStore(dir)) and pass it.');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling createPlanCanvasServer() with no arguments; passing an options object without a store key ({ host, port, ... } but no store); passing store: null/undefined after a failed store initialization (e.g. store factory returned null).

Common situations: Wiring up the canvas server in a custom launcher and forgetting to create the store (e.g. new FileSessionStore(...) or equivalent) first; destructuring config where the store field is absent; a store module failing to load and silently yielding undefined.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/8f28a8f04a691379. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/plan-canvas/server.js:126

      "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-src 'self'";
  }
  res.writeHead(statusCode, headers);
  res.end(html);
}

function createPlanCanvasServer({
  store,
  host = DEFAULT_HOST,
  version = '0.0.0',
  idleTimeoutMs = DEFAULT_IDLE_TIMEOUT_MS,
  heartbeatMs = 15000,
  thinkingStaleMs = DEFAULT_THINKING_STALE_MS,
  typingExpiryMs = DEFAULT_TYPING_EXPIRY_MS,
  presenceSweepMs = DEFAULT_PRESENCE_SWEEP_MS,
  onIdleShutdown = null,
  log = () => {}
} = {}) {
  if (!store) throw new Error('createPlanCanvasServer requires a session store');

  const allowedHostnames = buildAllowedHostnames(host);
  const wake = new EventEmitter();
  wake.setMaxListeners(0);
  const sseClients = new Map(); // key -> Set<res>
  const awaitCounts = new Map(); // key -> active long-poll count
  const workingKeys = new Map(); // key -> ms timestamp the agent took feedback
  const typingKeys = new Map(); // key -> ms timestamp the agent signalled composing
  const watchers = new Map(); // key -> fs.FSWatcher
  const lastPresence = new Map(); // key -> last broadcast state, for sweep diffing
  let idleTimer = null;
  let presenceSweep = null;
  let closed = false;

  // --- presence + SSE ---------------------------------------------------

  /**
   * Presence never claims more than the server actually knows:

View on GitHub (pinned to 8321021c54)