hcengineering/platform · error · ApiError

ApiError

Error message

ApiError

What it means

The analytics-collector's wrapRequest wrapper extracts a bearer token from request headers; if extractToken returns undefined, it throws ApiError(401), rejecting unauthenticated requests before the handler runs. This is the service's authentication gate for all wrapped routes.

Source

Thrown at services/analytics-collector/pod-analytics-collector/src/server.ts:38

import { Token } from '@hcengineering/server-token'
import cors from 'cors'
import express, { type Express, type NextFunction, type Request, type Response } from 'express'
import { type Server } from 'http'
import config from './config'
import { ApiError } from './error'
import { geoFieldMapping, getAllPossibleIps, getClientIp, getGeoLocationFromIp } from './geoip'

type AsyncRequestHandler = (req: Request, res: Response, token: Token, next: NextFunction) => Promise<void>

const handleRequest = async (
  fn: AsyncRequestHandler,
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> => {
  const token = extractToken(req.headers)
  if (token === undefined) {
    throw new ApiError(401)
  }
  try {
    await fn(req, res, token, next)
  } catch (err: unknown) {
    next(err)
  }
}

const wrapRequest = (fn: AsyncRequestHandler) => (req: Request, res: Response, next: NextFunction) => {
  void handleRequest(fn, req, res, next)
}

function isContentValid (body: any[]): boolean {
  return !body.some((it) => {
    if (it == null) return true
    if (!('event' in it)) return true
    if (!('properties' in it)) return true
    if (!('timestamp' in it)) return true

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Send a valid Authorization header (e.g. 'Bearer <token>') on every request to wrapped routes
  2. Inspect the raw request headers (curl -v / gateway logs) to confirm the header survives proxies
  3. Match the header format expected by extractToken in services/analytics-collector/pod-analytics-collector/src/server.ts
  4. Verify API client/SDK configuration includes the auth token for the correct environment

Example fix

// before
curl -X POST https://collector/collect -d '[...]'
// after
curl -X POST https://collector/collect -H 'Authorization: Bearer <token>' -d '[...]'
Defensive patterns

Strategy: validation

Validate before calling

// check the token exists before sending
if (!authToken) throw new Error('Analytics collector auth token is not configured')
const headers = { Authorization: `Bearer ${authToken}`, 'Content-Type': 'application/json' }

Try / catch

try {
  await collector.post('/collect', body, { headers })
} catch (err) {
  if (err.response?.status === 401) {
    // refresh token / fail fast with a clear config error
  } else throw err
}

Prevention

When it happens

Trigger: POST /collect (or any wrapped route) called without an Authorization header, with a malformed header that extractToken cannot parse, or with an empty token.

Common situations: Client forgot to set the Authorization header; a proxy/gateway stripped the header; SDK or curl command omits the token; header prefix (e.g. 'Bearer ') mismatched with what extractToken expects.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/ffd45b1784b2f6bd. Report an issue: GitHub.