﻿# Error Codes Catalog

This file defines canonical machine-readable error codes for Duuble API v1.
Use these codes in all route docs and implementation responses.

## Global
- `UNAUTHORIZED` -> `401`: missing/invalid/expired access token.
- `FORBIDDEN` -> `403`: authenticated but action not permitted.
- `NOT_FOUND` -> `404`: resource does not exist.
- `VALIDATION_FAILED` -> `422`: request shape/constraints invalid.
- `CONFLICT` -> `409`: state conflict.
- `GONE` -> `410`: resource existed but is no longer usable, usually because it expired.
- `RATE_LIMITED` -> `429`: throttled by abuse/rate policy.
- `INTERNAL_ERROR` -> `500`: unexpected server failure (canonical 500 code emitted from a known business path).
- `INTERNAL_SERVER_ERROR` -> `500`: unexpected server failure raised by the global exception handler when no domain code applies. Clients should treat this as equivalent to `INTERNAL_ERROR`.
- `SERVICE_UNAVAILABLE` -> `503`: temporary outage.

## Auth
- `LOGIN_CONFIG_UNAVAILABLE` -> `503`
- `OTP_RATE_LIMITED` -> `429`
- `OTP_INVALID_OR_EXPIRED` -> `400`
- `REFRESH_TOKEN_INVALID` -> `401`
- `GOOGLE_TOKEN_INVALID` -> `401`: Google ID token failed validation. Returned by the (currently disabled) Google sign-in path; reserved here so codes stay stable when the endpoint is re-enabled.
- `APPLE_TOKEN_INVALID` -> `401`: Apple ID token failed validation. Returned by the (currently disabled) Apple sign-in path; reserved here so codes stay stable when the endpoint is re-enabled.
- `FIREBASE_TOKEN_INVALID` -> `401`: Firebase ID token failed validation. Returned by `POST /auth/firebase/token` when a custom-token-exchange flow is used.

## Users / Social Graph
- `USER_NOT_FOUND` -> `404`
- `USERNAME_TAKEN` -> `409`
- `ALREADY_FOLLOWING` -> `409`
- `CANNOT_FOLLOW_SELF` -> `422`
- `ALREADY_BLOCKED` -> `409`
- `CANNOT_BLOCK_SELF` -> `422`

## Posts / Comments / Voting
- `POST_NOT_FOUND` -> `404`
- `NOT_POST_OWNER` -> `403`
- `COMMENT_NOT_FOUND` -> `404`
- `NOT_COMMENT_OWNER` -> `403`
- `VOTE_NOT_ALLOWED` -> `403`
- `VOTE_ALREADY_CAST` -> `409`
- `POST_NOT_VOTABLE` -> `409`
- `INVALID_SLIDER_VALUE` -> `422`
- `REPOST_NOT_ALLOWED` -> `409`
- `POST_SHARE_NOT_ALLOWED` -> `403`

## Hubs
- `HUB_NOT_FOUND` -> `404`
- `HUB_LINK_TAKEN` -> `409`
- `HUB_ROLE_FORBIDDEN` -> `403`
- `HUB_MEMBERSHIP_REQUIRED` -> `403`
- `HUB_SUPER_ADMIN_REQUIRED` -> `403`
- `CANNOT_REMOVE_SUPER_ADMIN` -> `409`
- `HUB_INVITATION_NOT_FOUND` -> `404`
- `HUB_INVITATION_EXPIRED` -> `410`
- `HUB_INVITATION_ALREADY_HANDLED` -> `409`
- `HUB_INVITE_LINK_NOT_FOUND` -> `404`
- `HUB_INVITE_LINK_EXPIRED` -> `410`
- `HUB_JOIN_REQUEST_NOT_FOUND` -> `404`
- `HUB_JOIN_REQUEST_ALREADY_HANDLED` -> `409`
- `HUB_DISCUSSION_NOT_FOUND` -> `404`

## Uploads
- `UPLOAD_POLICY_VIOLATION` -> `422`
- `UPLOAD_NOT_FINALIZABLE` -> `409`
- `UPLOAD_CHECKSUM_MISMATCH` -> `422`
- `INVALID_IMAGE_REFERENCE` -> `422`

## Reports / Notifications
- `REPORT_REASON_INVALID` -> `422`
- `CANNOT_REPORT_SELF` -> `422`: user attempts to report their own profile or a post they authored.
- `NOTIFICATION_NOT_FOUND` -> `404`
- `SSE_NOT_SUPPORTED_426` -> `426`: client does not advertise SSE; ask it to upgrade the connection.
- `SSE_NOT_SUPPORTED_400` -> `400`: SSE stream cannot be established for this request (malformed handshake or unsupported transport).

(The two `SSE_NOT_SUPPORTED_*` codes replace the historical single `SSE_NOT_SUPPORTED` entry. The split lets the server pick the correct HTTP status without overloading one code, and lets the client distinguish "your transport does not support SSE — upgrade" from "this request cannot start an SSE stream right now".)

## Minimum Required Errors Per Endpoint
- `GET` resource endpoint: `401` (if private), `404`
- `POST` create/action endpoint: `401`, `403` (permissioned), `422`, `429` (when abuse-sensitive)
- `PUT/PATCH` update endpoint: `401`, `403`, `404`, `409`, `422`
- `DELETE` endpoint: `401`, `403`, `404`
