yikart/AiToEarn · error · UnauthorizedException
Unauthorized
Error message
Unauthorized
What it means
The request-context interceptor stores the authenticated user in AsyncLocalStorage per request. The getUser() helper throws Nest's UnauthorizedException (401) when the store has no user, i.e. the request reached a protected endpoint without a valid authenticated session.
Source
Thrown at project/aitoearn-backend/libs/common/src/interceptors/request-context.interceptor.ts:41
const SUPPORTED_LANGUAGES: Locale[] = ['en-US', 'zh-CN']
export function getLocale(): Locale {
return requestContext.getStore()?.locale || 'en-US'
}
export function getRequestContext(): RequestContextStore | undefined {
return requestContext.getStore()
}
/**
* Get authenticated user from request context.
* Throws UnauthorizedException if user is not authenticated.
* Use this for protected endpoints that require authentication.
*/
export function getUser(): TokenInfo {
const user = requestContext.getStore()?.user
if (!user) {
throw new UnauthorizedException()
}
return user
}
/**
* Get authenticated user from request context, or undefined if not authenticated.
* Does not throw. Use this for public endpoints that optionally use user info.
*/
export function getUserOptional(): TokenInfo | undefined {
return requestContext.getStore()?.user
}
@Injectable()
export class RequestContextInterceptor implements NestInterceptor {
public intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
const locale = this.parseLocale(context)
const user = this.extractUser(context)
return requestContext.run({ locale, user }, () => next.handle())View on GitHub (pinned to d3aa8bea5b)
Solutions
- Attach a valid Authorization: Bearer <token> header to the request
- Re-authenticate or refresh the token if expired
- Verify the auth middleware/guard runs and populates requestContext.user for this route
- Ensure the API key/token matches the environment (CN key with .cn URLs, international key with .ai URLs)
Example fix
// before
const res = await fetch('https://aitoearn.cn/api/v1/user');
// after
const res = await fetch('https://aitoearn.cn/api/v1/user', {
headers: { Authorization: `Bearer ${token}` },
}); Defensive patterns
Strategy: validation
Validate before calling
const headers = { Authorization: `Bearer ${token}` };
if (!token) throw new Error('Missing auth token: login first');
const res = await fetch(url, { headers });
if (res.status === 401) await reauthenticate(); Type guard
function hasUser(ctx?: { user?: TokenInfo }): ctx is { user: TokenInfo } {
return !!ctx?.user;
} Try / catch
try {
const data = await api.call();
} catch (e) {
if (e.response?.status === 401) {
await refreshTokenOrLogin();
return api.call();
}
throw e;
} Prevention
- Always attach a valid Bearer token to authenticated endpoints
- Refresh tokens before expiry; handle 401 with re-login flow
- Match API key/token to environment (.cn key with .cn URLs, .ai key with .ai URLs)
- Ensure auth middleware populates requestContext before guarded handlers run
When it happens
Trigger: Calling an endpoint that calls getUser() without a Bearer token, with an expired/invalid token, or the auth guard/context middleware did not run before the interceptor populated the context.
Common situations: Missing Authorization header in API clients; expired API keys; third-party calls (e.g. webhooks, MCP) that bypass the auth middleware; environment mismatch where a token issued for one environment is used against another (aitoearn.cn vs aitoearn.ai).
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/a1ced9ba422dc310.
Report an issue: GitHub.