Skip to content

Unable to add Agent or Credentials in fresh install #345

@shindakun

Description

@shindakun

Creating an agent fails with 401 "Invalid API key or token" on a fresh self-hosted (local mode) install

Just came across this project and wanted to check it out, but had an issue actually creating an agent when running locally. Fresh self-hosted instance, default local mode, and clicking "Create Agent" in the dashboard returns 401 "Invalid API key or token". It turns out the same thing happens for basically every project-scoped action in the dashboard (listing/adding secrets, etc.), so the self-hosted UI is mostly unusable. See the affected-endpoints list below.

Version: 1.35.0 (also reproduces on a clean DB).

Repro

Fresh install, nothing custom:

git clone https://github.com/onecli/onecli
cd onecli
docker compose -f docker/docker-compose.yml up -d

No .env, no NEXTAUTH_SECRET, so it comes up in local mode. The container's /app/data/runtime-config.json is {"authMode":"local","oauthConfigured":false}, and GET /v1/auth/session returns the local admin fine:

curl -s http://localhost:10254/v1/auth/session
# {"id":"...","email":"admin@localhost","name":"Admin","projectId":"<PID>","organizationId":"..."}

But creating an agent (what the dashboard POSTs) 401s:

curl -s -w '\n%{http_code}\n' -X POST http://localhost:10254/v1/agents \
  -H 'Content-Type: application/json' \
  --data-raw '{"name":"test","identifier":"test"}'
# {"error":{"message":"Invalid API key or token.","type":"authentication_error"}}
# 401
Image

Add the x-project-id header (value from /v1/auth/session above) and the exact same request works:

curl -s -w '\n%{http_code}\n' -X POST http://localhost:10254/v1/agents \
  -H 'Content-Type: application/json' \
  -H 'x-project-id: <PID>' \
  --data-raw '{"name":"test","identifier":"test"}'
# {"id":"...","name":"test","identifier":"test","createdAt":"..."}
# 201

So the only difference between 401 and 201 is the x-project-id header, which the dashboard never sends in local mode.

Affected endpoints

This is not specific to creating agents. Every route that goes through authMiddleware 401s the same way without x-project-id. Confirmed by hand:

  • POST /v1/agents (create agent)
  • GET /v1/agents (list agents)
  • GET /v1/secrets (list secrets / the LLM connections page)
  • POST /v1/secrets (add a credential)

Looking at packages/api/src/app.ts, the only routes that do NOT use authMiddleware are /v1/health and /v1/auth/session. Everything else does: agents, secrets, rules, user, apps, gateway, gateway-url, container-config, counts, skill, credential-stubs. So in practice the whole project-scoped dashboard is unusable in local mode, not just agent creation. I only reproduced the four above by hand, but the rest go through the same middleware, so there are likely more.

Cause

The project-scoped routes require x-project-id, but the self-hosted middleware never sets it.

  • packages/api/src/middleware/auth/resolve.ts:40-41 returns null when the header is absent:

    const headerProjectId = request.headers.get("x-project-id");
    if (!headerProjectId) return null;
  • packages/api/src/middleware/auth/session.ts:26 then bails, so authMiddleware returns 401:

    if (!projectId && requireProject) return null;
  • apps/web/src/proxy.ts is what would inject the header, but it only does so for cloud. The non-cloud branch returns early before the injection block:

    // proxy.ts
    if (!IS_CLOUD) {
      // ...scope-strip + redirect...
      return NextResponse.next();          // returns here, no x-project-id set
    }
    // header injection (requestHeaders.set("x-project-id", projectId)) only runs below, cloud-only

    The matcher also excludes v1 (apps/web/src/proxy.ts:82), so the API requests are not rewritten by middleware at all.

Net: in local/self-hosted mode nothing supplies x-project-id, but authMiddleware requires it for project-scoped routes, so the dashboard can create nothing.

Notes

  • GET /v1/auth/session works because it does not go through authMiddleware (it calls the session provider directly), which is why login looks fine but every project-scoped action fails. That makes it look like an auth/login problem when it is actually a missing header.
  • The README does not mention the .env file at all, so the first guess is that .env/AUTH_MODE setup is the problem. It is not: the entrypoint already derives authMode: "local" when NEXTAUTH_SECRET is empty, with or without an .env. Worth a line in the README either way.
  • Also, changing Agents in the "Getting Started" model doesn't appear to do anything currently, if you switch to "Autonomous Agents" and then close the modal, next time you open it, it has returned to "Coding Agents".

Workaround

Until fixed, project-scoped calls work if you pass x-project-id from /v1/auth/session. Not usable from the dashboard UI without an extension that injects the header.

Suggested fix

In local mode, resolve the user's default project when x-project-id is absent (the local admin has exactly one), rather than returning null. Either in resolveProjectId (fall back to the default project for the local user) or by having the self-hosted path in proxy.ts inject x-project-id the way the cloud path does.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions