vxcontrol/pentagi · error

token can't be empty

Error message

token can't be empty

What it means

After confirming the Authorization header starts with "Bearer ", the middleware slices off the 7-character prefix and rejects the request when the remainder is empty. This means the client sent `Authorization: Bearer` (or `Bearer ` with only whitespace-stripped empty content) but no actual token value. The middleware cannot authenticate without a credential, so it returns authResultSkip for other mechanisms to attempt.

Source

Thrown at backend/pkg/server/auth/auth_middleware.go:206

		c.Set("cpt", "automation")
	}

	return authResultOk, nil
}

const PrivilegeAutomation = "pentagi.automation"

func (p *AuthMiddleware) tryProtoTokenAuthentication(c *gin.Context) (authResult, error) {
	authHeader := c.Request.Header.Get("Authorization")
	if authHeader == "" {
		return authResultSkip, errors.New("token required")
	}

	if !strings.HasPrefix(authHeader, "Bearer ") {
		return authResultSkip, errors.New("bearer scheme must be used")
	}
	token := authHeader[7:]
	if token == "" {
		return authResultSkip, errors.New("token can't be empty")
	}

	// skip validation if using default salt (for backward compatibility)
	if p.globalSalt == "" || p.globalSalt == "salt" {
		return authResultSkip, errors.New("token validation disabled with default salt")
	}

	// try to validate as API token first (new format with JWT signing key)
	apiClaims, apiErr := ValidateAPIToken(token, p.globalSalt)
	if apiErr != nil {
		return authResultFail, errors.New("token is invalid")
	}

	// check token status and get privileges through cache
	status, privileges, err := p.tokenCache.GetStatus(apiClaims.TokenID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {

View on GitHub (pinned to ea665308ba)

Solutions

  1. Ensure the token variable is actually set before the request: check `echo -n "${API_TOKEN}" | wc -c` is non-zero.
  2. Send `Authorization: Bearer <non-empty api token>` created from the API tokens settings UI.
  3. Guard client code: skip the request or fail fast with a clear config error if the token is empty.
  4. Regenerate an API token if the previous one was deleted from the database.

Example fix

// before
const header = `Bearer ${process.env.API_TOKEN}`;

// after
if (!process.env.API_TOKEN) throw new Error("API_TOKEN is not set");
const header = `Bearer ${process.env.API_TOKEN}`;
Defensive patterns

Strategy: validation

Validate before calling

const token = process.env.API_TOKEN ?? "";
if (!token.trim()) throw new Error("API token is empty; refusing to send request");
headers["Authorization"] = `Bearer ${token.trim()}`;

Type guard

function hasToken(t: unknown): t is string {
  return typeof t === "string" && t.trim().length > 0;
}

Try / catch

try {
  return await client.request(opts);
} catch (e) {
  if (is401(e) && /can't be empty|token required/i.test(e.message)) {
    throw new ConfigError("API token missing — check API_TOKEN secret");
  }
  throw e;
}

Prevention

When it happens

Trigger: Sending `Authorization: Bearer` or `Authorization: Bearer ` (trailing space, empty token), typically when a template variable holding the token is empty/unset, e.g. `Bearer ${API_TOKEN}` where API_TOKEN="".

Common situations: CI/CD secret not injected so the token env var is empty; .env file missing the token; string interpolation in a config file silently producing "Bearer "; token deleted from settings and the client cached an empty value.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/b519745c4f81d0da. Report an issue: GitHub.