Reference / FAQ

FAQ & troubleshooting

Concrete answers to the questions people ask in their first week, each with the exact error text where there is one, the fix, and where to read more.

Tools and capabilities

I added a tool to composition.ts but the agent can’t call it

components: composeAgent(...) is a catalogue of what an instance may use. Shared tools (*.tool.ts), applications and MCP servers are mounted only on instances that install them; until then the model sees no such tool, and a call fails with No tool called lookup_order exists. Pick one:

  • The tool should exist on every instance: put a plain Glove tool (name, description, inputSchema, do) in the definition’s tools field instead.
  • It should be per instance: install it with install(def, config) in defineAgentInstance, installations on client.agent(route).create, client.install(agentId, { kind, id, config }), or PUT /api/installations.
terminalbash
curl -X PUT http://127.0.0.1:4141/api/installations \
  -H 'content-type: application/json' \
  -d '{"agentId":"support-eu","kind":"tool","id":"lookup-order","config":{"region":"eu"}}'

The next run of that instance has the tool; no restart needed. The inspector shows an instance’s installations but cannot add them. Memory profiles, layers and subscribers are different: they need the catalogue and the memory/layers/subscribers field, never an installation. Full table in Project structure.

Installing fails with “Unknown Foundry tool capability”

The id is not in the catalogue of that instance’s definition. Check that the definition’s composeAgent(...) includes it and that you used the file-derived id: tools/calendar/today.tool.ts is calendar/today, not the tool’s model-facing name. Config is checked later: a value that fails the tool’s zod schema fails each run with Invalid config for tool "…" and the zod issues.

“Foundry tool identity has not been bound yet”

A definition gets its id only when Foundry discovers it: a default export from a convention file (*.tool.ts, *.app.ts, …) inside an agent’s folder. Defining it in composition.ts, exporting it by name, keeping it in a folder that contains no agent.ts, or calling install() from a separate client app all leave it unbound. Move it into agents/<agent>/tools/<name>.tool.ts, and from client apps send the plain { kind, id, config } object.

My app’s send tool fails with “agent has no enabled bindings”

Outbound delivery (an installed application’s glove_app_…_send tool, glove_foundry_transmit, or POST /api/transmissions/:route/deliver) is authorized per run from the instance’s enabled bindings. Add a defineBinding({ id, agent, application, transmission, account, route, capabilities, enabled: true }) for that instance and outbound route to bindings in foundry.application.ts (or client.putBinding). Without a binding for the route, delivery fails with Run "…" is not authorized for outbound route "…". See Apps & transmissions.

State and persistence

My agent forgets earlier messages, and WebChat shows no history

Every run executes in its own worker process. A store that returns new MemoryStore(...) — what the starters do — is empty at the start of each run, so each turn sees only the current message and the transcript endpoint returns nothing (with persisted: true). Caching stores in a module-level Map does not help either. Return a store backed by something outside the process: the small FileStore from the tutorial for one host, or a database-backed StoreAdapter. A definition’s store takes precedence over the application’s conversationStore.

My instances and conversations disappear on restart

Your application uses MemoryFoundryDataAdapter (or no data at all), which lives in the runtime process. glove foundry dev restarts that process whenever you save a file in agents/, .env* or a root config file, so instances, conversations, installations and schedules vanish. Projects created by glove-foundry init avoid this with file-backed adapters under .data/. Persist four things, separately:

WhatConfigured bySingle-host option
Runtime data: instances, conversations, installations, activations, workspace entries, inbox, tasksdata in defineApplicationnew FileFoundryDataAdapter({ file: ".data/foundry.json" })
Transcripts (Glove messages)store on the definition, or conversationStoreA file- or database-backed StoreAdapter
Structured memoryYour memory profile adapterscreateSqliteMemoryAdapters from glove-memory/sqlite (Node 22.13+)
Working-environment files (VFS)persistence on defineWorkingEnvironmentfoundryDataEnvironmentPersistence(...) over a durable data adapter

A durable data adapter does not make an in-memory transcript store durable, and vice versa. Several hosts need a shared, transactional adapter. See Persistence.

Foundry won’t start: “Agent instance … references unknown definition …”

Durable data outlives code. If you rename or delete an agent folder, startup fails with that error; if you remove a tool, app or MCP file that an instance still installs, it fails with Unknown Foundry tool capability. Restore the file, start the runtime, and remove the reference first (client.uninstall, or client.configureAgent(id, { installations })), or edit the record in your data store (.data/foundry.json for the file adapter).

Running the server

Foundry refuses to bind 0.0.0.0

terminaltext
Foundry refuses to bind 0.0.0.0 without application.requestAuthorization. Keep the runtime on loopback or provide a user-owned authorization adapter.

The default host is 127.0.0.1. Any non-loopback server.host or --host requires an authorization adapter in foundry.application.ts. It sees the method, path, query, Authorization header, cookie and remote address of every request — API, inspector, health checks and event streams — and returns only a boolean. A denial is a 401 with your challenge as WWW-Authenticate.

foundry.application.tstypescript
import { Effect } from "effect";
import { defineApplication } from "glove-foundry";

export default defineApplication({
  name: "support-desk",
  requestAuthorization: {
    identifier: "shared-bearer-token",
    challenge: 'Bearer realm="support-desk"',
    authorize: (request) =>
      Effect.sync(() => {
        const token = process.env.FOUNDRY_CONTROL_TOKEN;
        return Boolean(token) && request.authorization === `Bearer ${token}`;
      }),
  },
});

Callers then send the header: createFoundryClient({ authorization: { identifier, headers: () => ({ authorization: … }) } }), or the OpenAI SDK’s apiKey. Put TLS and a firewall in front. See Deploy & security.

Port 4141 is already in use

terminaltext
Error: listen EADDRINUSE: address already in use 127.0.0.1:4141

Another process (often a forgotten pnpm dev) holds the port, and the failed runtime may keep running until you press Ctrl+C. Stop the other process, or pick another port with pnpm dev --port 4142 (npm run dev -- --port 4142) or server: { port: 4142 } in foundry.config.ts. Then point clients at it (baseUrl, FOUNDRY_URL, the OpenAI baseURL).

Which Node version do I need? I get SQLite errors

Foundry requires Node 22 or newer ("engines": { "node": ">=22" }); 22.13+ is recommended. glove-memory/sqlite uses Node’s built-in node:sqlite, which needs 22.13+, so earlier 22.x releases fail when that module loads. Check node --version and upgrade.

TypeScript says two JsSession (or Glove) types are incompatible

Your project has two copies of a Glove package, typically glove-js: one that glove-foundry depends on and one you added with a different version. Classes from different copies do not type-match. Keep glove-core, glove-js, glove-memory, glove-working-environment and friends on the versions your installed glove-foundry declares (see node_modules/glove-foundry/package.json), reinstall, and confirm one copy with pnpm why glove-js or npm ls glove-js. init writes those versions for you and prints a warning when it had to fall back to built-in defaults.

Calling agents

How do I call an agent from my app?

UseWhenShape
Typed client, glove-foundry/clientA TypeScript backend that manages instances, conversations, installations, approvals and transcriptscreateFoundryClient<FoundryRoutes>(), agent(route).create, createConversation, send, run.wait()
OpenAI-compatible, /v1/chat/completions and /v1/responsesExisting OpenAI SDK code, any language, chat UIsmodel = instance id; continue with x-foundry-conversation-id, conversation_id or user
Automation API, /v1/runsJob runners and other services that start work and follow itPOST /v1/runs { model, input } → 202; GET /v1/runs/:id, /events (SSE with Accept: text/event-stream), POST /stop, /steer

All three resolve to a persisted instance and write into a durable conversation, and all three show up in the inspector. From a browser, go through your own server route rather than exposing the runtime. See Client & HTTP API and OpenAI-compatible API.

run.wait() throws “Timed out waiting for Foundry run”

wait() polls every 150 ms for up to 60 s by default. Pass run.wait({ timeoutMs: 300_000 }) for longer work. Timing out does not cancel the run (use run.cancel()). A run paused on an approval stays running until someone decides.

Where do API keys and other credentials go?

  • Model provider keys are environment variables your model adapter reads, e.g. OPENROUTER_API_KEY. The CLI loads .env and then .env.local from the project root into the runtime and every run’s worker, never overwriting a variable that is already set — so the shell beats both files, and .env beats .env.local for the same key. The dev server restarts when either file changes.
  • Integration credentials never enter Foundry. An account (defineAccount) holds metadata and an opaque accessRef; your accountSessions adapter on the agent definition turns that into a live session inside withAccountSession. MCP auth comes from your mcpAdapter (getAuthHeaders, getAccessToken, getStdioEnvironment).
  • Never put secrets in installation config, instance context, prompts or manifests; those are stored and displayed. Incoming HTTP auth is requestAuthorization, which also stores nothing.

Sending an image returns 413 “Request body exceeds the 1048576-byte limit”

JSON bodies are capped at 1 MB by default, and base64 adds about a third to a file’s size. Raise the cap for trusted multimodal clients:

foundry.config.tstypescript
import { defineConfig } from "glove-foundry/config";

export default defineConfig({
  server: { port: 4141, messageBodyBytes: 40 * 1024 * 1024 },
});

Allowed values are 1048576 to 134217728 (128 MB). The setting applies to message-bearing endpoints — POST /api/conversations/:id/messages, run steering, /v1/chat/completions, /v1/responses, /v1/runs and its steer route — while other endpoints stay at 1 MB. For large media, prefer URL sources over base64.

Schedules and approvals

Why didn’t my schedule fire, and where do I see it?

  • It was never armed. A schedules entry becomes an activation only when that instance next runs. An instance that has never run has none.
  • It was lost. Activations live in the data adapter; with MemoryFoundryDataAdapter every restart (including dev hot restarts) drops them. Use a durable adapter. Activations fire only while the runtime is running.
  • Wrong clock. Cron timezone defaults to UTC.
  • It was paused or cancelled. The agent can pause, resume and cancel its own schedules with glove_foundry_schedules. Once a definition schedule’s activation is cancelled for an instance, Foundry does not re-arm it for that instance.

See them on the inspector’s Automations page, with client.activations(workspaceId), or at GET /api/activations?workspace=…. A run it starts appears in Runs with source activation. See Playbooks, schedules & sleep.

My permission-gated tool never asks, or is always denied

Foundry turns requiresPermission into an approval record only through its default display manager; if the definition sets displayManager, requests go to yours instead. Records expire after 5 minutes and do not survive a runtime restart. Programmatic tool calling from a REPL refuses to run a tool whose input needs approval; the model must call it directly. Denied, expired and undeliverable requests fail closed: the tool does not run and the model receives Permission denied for tool "…". List pending requests with client.approvals({ status: "pending" }) or on the inspector’s Overview. See Approvals & steering.

Choosing Foundry

Do I need Foundry, or is glove-core enough?

glove-core is the agent loop: a model adapter, tools, a store and a display manager, running inside your process. Use it alone to embed one agent in an existing server, a Next.js route, a React app or a script, where you own hosting and state.

Add Foundry when you need what sits around the loop: many persisted instances of one definition, durable conversations, per-instance installations, schedules and sleep, inbound and outbound transports, human approvals, an inspector with WebChat, a typed client, and OpenAI-compatible endpoints. A Foundry agent is still a Glove agent — its tools are Glove tools and its store is a Glove StoreAdapter — so you can start with glove-core and move later.