IodidDOCUMENTATION

reference/errors.mdx

/doc/reference/errors

PUBLIC DOCUMENTATION

Reference — Errors

HTTP error catalog, pending codes, LolApiError shape and LolApiExceptionHandler mapping.

Shape — LolApiError

LolApiError (src/main/java/com/safjnest/spring/dto/LolApiError.java:5) — sole Spring DTO for errors (ADR-0005/0007):

java
public record LolApiError(int status, String code, String message, ResponseMetadata metadata) {
    public LolApiError(int status, String code, String message) { this(status, code, message, null); }
}

ResponseMetadata (src/main/java/com/safjnest/lol/model/ResponseMetadata.java:3): Pagination pagination, Long lastUpdate, Boolean refresh, Filter filter — optional, populated only for 202 PENDING with context.

json
{
  "status": 202,
  "code": "profile_pending",
  "message": "Profile initialization is pending",
  "metadata": {
    "pagination": null,
    "lastUpdate": null,
    "refresh": true,
    "filter": null
  }
}

The second constructor without metadata is used for 400/404/405 from LolApiExceptionHandler.

HTTP catalog

HTTPcodeExample messageSourceFile:line
400invalid_requestInvalid rank: must be one of: IRON, BRONZE, ...LolApiParameters.invalidLolApiParameters.java:235
400invalid_requestMissing required query parameter 'q'MissingServletRequestParameterExceptionLolApiExceptionHandler.java:31
400invalid_requestMissing required path parameter 'puuid'MissingPathVariableException:40
400invalid_requestInvalid value for parameter 'page'MethodArgumentTypeMismatchException:49
400invalid_requestInvalid shard: is requiredrequiredShardLolApiParameters.java:163
400invalid_requestInvalid patch: must have the form major.minorpatch:103
400invalid_requestInvalid limit: must be between 1 and 50limit:203
400invalid_requestInvalid sort: must be timeStart:asc or timeStart:descmatchOrder:222
400invalid_requestInvalid role: must be one of: TOP, JUNGLE, MID, BOT, UTILITYrole:168
400invalid_requestInvalid role: is not supported by the selected queuevalidateRole:177
404not_foundProfile not foundLolApiResponses NOT_FOUNDLolApiResponses.java:24
404not_foundMatch not foundMatchService.getDetailLolController.java:287
404not_foundEndpoint not foundNoHandlerFoundExceptionLolApiExceptionHandler.java:67
405method_not_allowedHTTP method 'POST' is not supported for this endpointHttpRequestMethodNotSupportedException:58
202*_pending (see below)Profile initialization is pendingLolApiResponses PENDINGLolApiResponses.java:22
500internal_errorUnexpected server errorExceptionLolApiExceptionHandler.java:76

codeFor (LolApiExceptionHandler.java:89):

java
private String codeFor(HttpStatusCode status) {
    return switch (status.value()) {
        case 400 -> "invalid_request";
        case 404 -> "not_found";
        case 405 -> "method_not_allowed";
        default -> "request_failed";
    };
}

Validation — LolApiParameters.invalid

java
// LolApiParameters.java:235
public static ResponseStatusException invalid(String parameter, String reason) {
    return new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid " + parameter + ": " + reason);
}

All validation methods (rank, queue, patch, page, limit, matchLimit, matchOffset, matchOrder, activityFilter, otpChampion, recordMetric, role) throw invalid → 400 with prefix Invalid {parameter}:.

202 pending — codes

LolApiResponses.from (src/main/java/com/safjnest/spring/controller/LolApiResponses.java:14):

java
case PENDING -> ResponseEntity.status(HttpStatus.ACCEPTED)
    .body(new LolApiError(HttpStatus.ACCEPTED.value(), pendingCode, pendingMessage, result.metadata()));

Each controller passes its own pendingCode/pendingMessage:

codemessageController:lineMeaningRetry
profile_pendingProfile initialization is pendingLolController.java:292SummonerView not yet computed, job in ComputeScheduler/RiotScheduler3-5s
profile_activity_pendingProfile activity is being prepared:178ProfileActivity computing2-3s
profile_matchups_pendingProfile matchups are being prepared:202ProfileMatchups computing2-3s
profile_records_pendingProfile records are being prepared:214ProfileRecordPage for profile computing2-3s
match_pendingMatch analysis is pending:285Match not in Mongo, queued on SyncScheduler3-5s
leaderboard_pendingLeaderboard data is being preparedLeaderboardController.java:54LeaderboardPage rebuilding ZSET2-3s
champion_tier_list_pendingChampion tier list is being preparedChampionController.java:45ChampionTierList computing CHAMPION5-10s
champion_data_pendingChampion data is being prepared:73ChampionView/ChampionStatistics computing5-10s
champion_pendinggeneric—alias for champion—

metadata for 202 contains refresh=true and often lastUpdate/filter for debugging. GET /profile/{puuid} also adds header X-Profile-Refresh: true (LolController.java:294).

http
HTTP/1.1 202 Accepted
Content-Type: application/json
X-Profile-Refresh: true

{
  "status": 202,
  "code": "profile_activity_pending",
  "message": "Profile activity is being prepared",
  "metadata": { "lastUpdate": 1715434800000, "refresh": true, "filter": { "champion": 157 } }
}

Mapping — LolApiExceptionHandler

LolApiExceptionHandler (src/main/java/com/safjnest/spring/controller/LolApiExceptionHandler.java:20) with @RestControllerAdvice:

HandlerExceptionReturnFile:line
handleResponseStatusResponseStatusExceptionerror(status, codeFor(status), reason):22
handleMissingParameterMissingServletRequestParameterException400 invalid_request Missing required query parameter 'x':31
handleMissingPathVariableMissingPathVariableException400 invalid_request Missing required path parameter 'x':40
handleTypeMismatchMethodArgumentTypeMismatchException400 invalid_request Invalid value for parameter 'x':49
handleMethodNotSupportedHttpRequestMethodNotSupportedException405 method_not_allowed HTTP method 'X' is not supported:58
handleNotFoundNoHandlerFoundException, NoResourceFoundException404 not_found Endpoint not found:67
handleAsyncRequestNotUsableAsyncRequestNotUsableExceptionvoid (client disconnected):72
handleUnexpectedException500 internal_error Unexpected server error + printStackTrace():76

error helper (:82):

java
private ResponseEntity<LolApiError> error(HttpStatusCode status, String code, String message) {
    String errorMessage = message != null && !message.isBlank() ? message : HttpStatus.valueOf(status.value()).getReasonPhrase();
    return ResponseEntity.status(status).body(new LolApiError(status.value(), code, errorMessage));
}

JSON examples

400 — validation

json
{
  "status": 400,
  "code": "invalid_request",
  "message": "Invalid patch: must have the form major.minor",
  "metadata": null
}
json
{
  "status": 400,
  "code": "invalid_request",
  "message": "Invalid limit: must be between 1 and 50",
  "metadata": null
}

404 — not found

json
{
  "status": 404,
  "code": "not_found",
  "message": "Profile not found",
  "metadata": null
}
json
{
  "status": 404,
  "code": "not_found",
  "message": "Endpoint not found",
  "metadata": null
}

405 — method not allowed

json
{
  "status": 405,
  "code": "method_not_allowed",
  "message": "HTTP method 'POST' is not supported for this endpoint",
  "metadata": null
}

202 — pending (with metadata)

json
{
  "status": 202,
  "code": "match_pending",
  "message": "Match analysis is pending",
  "metadata": { "lastUpdate": null, "refresh": true, "filter": null }
}

500 — internal

json
{
  "status": 500,
  "code": "internal_error",
  "message": "Unexpected server error",
  "metadata": null
}

Client handling

js
async function callApi(url) {
  const res = await fetch(url);
  if (res.status === 202) {
    const err = await res.json(); // LolApiError with code *_pending
    console.log("pending:", err.code, err.metadata);
    return { pending: true, err };
  }
  if (res.status === 400) {
    const err = await res.json();
    throw new Error(`Bad request: ${err.message} (${err.code})`);
  }
  if (res.status === 404) throw new Error("Not found");
  if (res.status === 405) throw new Error("Method not allowed");
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return { pending: false, data: await res.json() };
}

Always handle 202 as "data being prepared" — show skeleton/loading and retry with backoff. Do not treat 202 as an error.