Skip to content

CLI rejects workspace-scoped API tokens that are valid against GraphQL (v4.36.1 and v4.37.4) #845

@camerono

Description

@camerono

Summary

The Railway CLI returns

Invalid or expired Railway token. Please run 'railway login' to refresh your authentication

when authenticating via RAILWAY_API_TOKEN with a workspace-scoped API
token, even though the same token authenticates successfully against
https://backboard.railway.com/graphql/v2. An account-scoped ("No workspace")
token, minted from the same account at the same time, works fine with the CLI.

This forces users who want least-privilege scoped access to fall back to broader
account tokens, which defeats the purpose of workspace scoping.

This is not a duplicate of #699.
That issue reports account tokens failing on Linux; this issue reports
workspace tokens failing on macOS while account tokens from the same
account succeed. Same error string, different failure mode.

Reproduction

  1. In Account Settings → Tokens, mint two tokens from the same account:
    • Token A — Workspace dropdown set to <your workspace> (workspace-scoped)
    • Token B — Workspace dropdown set to No workspace (account-scoped)
  2. Test Token A with the CLI:
    export RAILWAY_API_TOKEN=<Token A>
    railway whoami      # → "Invalid or expired Railway token."
    railway list        # → same error
  3. Test Token B with the CLI:
    export RAILWAY_API_TOKEN=<Token B>
    railway whoami      # → succeeds
    railway list        # → succeeds
  4. Test Token A against GraphQL to prove the token itself is valid:
    curl -sS \
      -H "Authorization: Bearer <Token A>" \
      -H "Content-Type: application/json" \
      -d '{"query":"{ projects { edges { node { id name } } } }"}' \
      https://backboard.railway.com/graphql/v2
    → returns the expected projects payload for the workspace.

So the token is valid; the CLI is the component rejecting it.

Expected behavior

Workspace-scoped tokens should authenticate CLI operations that fall within
their scope (railway list, railway link, railway up, etc. scoped to
projects inside that workspace).

If some CLI endpoint genuinely requires account-wide scope (e.g. whoami
resolving across workspaces), the CLI should surface a clearer error — e.g.
"This command requires an account-scoped token; the provided token is scoped
to workspace <name>"
— rather than the generic "Invalid or expired", which
sends users down the wrong diagnostic path (they rotate tokens instead of
narrowing scope).

Environment

Field Value
CLI versions tested railway 4.36.1 and railway 4.37.4 (both fail identically; 4.37.4 was the latest release at repro time)
OS macOS 15 (Darwin 25.3.0)
Architecture arm64 (Apple Silicon)
Install method Homebrew (brew install railway)
Env var used RAILWAY_API_TOKEN (the CLI-wide account/workspace var — not RAILWAY_TOKEN, which is the project-token var)
Also reproduced via @railway/mcp-server (it wraps the CLI and inherits the failure); reproduced calling the CLI directly, so the MCP wrapper is not the cause

Verbose output

Run with debug logging, workspace token in env:

export RAILWAY_API_TOKEN=<Token A — workspace-scoped>
RUST_LOG=debug railway whoami 2>&1

Output (token redactedRUST_LOG=debug prints the full Authorization
header, so make sure to scrub it before pasting here):

<paste redacted debug output from the command above>

Impact

Blocks adoption of workspace-scoped tokens for any CLI-driven or CLI-wrapped
workflow:

  • Interactive development where a user wants a workspace-scoped token rather
    than a full account token on their dev machine.
  • Automated tooling that wraps the CLI — e.g. @railway/mcp-server, IDE
    integrations, internal scripts.
  • CI/CD that operates across multiple projects in a single workspace, where a
    project token is too narrow but an account token is broader than desired.

The currently viable fallbacks are:

  • Account tokens — work with the CLI but grant account-wide access.
  • Project tokens (RAILWAY_TOKEN) — work for railway up / railway run
    but are scoped to a single project + environment.

Neither covers the "scoped to one workspace" use case that workspace tokens
are meant to provide.

Notes for maintainers

  • Workspace and user IDs, plus a UTC timestamp of a failing attempt, are
    available on request — happy to share those via a Central Station Private
    Thread
    so the token/IDs aren't exposed publicly.
  • Raw token material will not be attached here; any debug output above has the
    Authorization header redacted.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions