Error Codes
All API errors are returned as JSON objects with an error field. Image
endpoints return Content-Type: application/json on error instead of the usualimage/png.
Error Response Format
Every error follows the same structure:
{
"error": "Human-readable error message"
}
There is no error code field, stack trace, or additional metadata. The HTTP
status code and the error message together identify the problem.
HTTP 400 -- Bad Request
Invalid Direction
Endpoints: /avatar, /ioshead, /iosbody
Returned when the direction parameter is not left or right:
GET /avatar/Notch/up/128
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Direction must be \"left\" or \"right\""
}
This is the only 400 error the API currently returns. All other invalid inputs
(nonexistent usernames, malformed UUIDs) result in 500 errors because they
manifest as failures during the Mojang/GeyserMC lookup rather than as
client-side validation errors.
HTTP 500 -- Internal Server Error
All 500 errors indicate that the server attempted to process the request but
encountered a failure during profile resolution, rendering, or database
operations.
Failed to Render Head
Endpoint: /head
{ "error": "Failed to render head" }
Causes:
- The username or UUID does not exist in the Mojang database.
- The Mojang API is unreachable or rate-limiting the server.
- The skin texture URL returned by Mojang is broken or unreachable.
- The skin PNG is corrupted and cannot be parsed by Sharp.
- Sharp encountered an internal error during extraction or resize.
Failed to Render Player
Endpoint: /player
{ "error": "Failed to render player" }
Causes are the same as head render failures, plus:
- Jimp failed to parse the skin buffer.
- A body part crop operation failed (unlikely unless the skin is malformed).
Failed to Render Avatar
Endpoint: /avatar
{ "error": "Failed to render avatar" }
Causes are the same as head render failures, plus:
- Canvas failed to load the skin image from the base64 data URL.
- An affine transform produced invalid geometry.
- Sharp's Lanczos3 resize of the canvas output failed.
Failed to Render iOS Head
Endpoint: /ioshead
{ "error": "Failed to render iOS head" }
Same causes as the avatar render error. This endpoint uses thecreateIsometricHeadRender function.
Failed to Render iOS Body
Endpoint: /iosbody
{ "error": "Failed to render iOS body" }
Same causes as the avatar render error. This endpoint uses thecreateIsometricBodyRender function.
Failed to Get Skin
Endpoint: /skin
{ "error": "Failed to get skin" }
Causes:
- Username/UUID does not exist.
- Mojang API unreachable.
- Skin texture URL unreachable.
Failed to Download Skin
Endpoint: /download
{ "error": "Failed to download skin" }
Same causes as the skin endpoint. The download endpoint uses the samegetRawSkin function but returns the file with a Content-Disposition header.
Failed to Get Stats
Endpoints: /allstats, /allstatsbedrock, /allstatsSorted
{ "error": "Failed to get stats" }
{ "error": "Failed to get bedrock stats" }
{ "error": "Failed to get sorted stats" }
Causes:
- Database connection lost.
- Stats table does not exist (should not happen if initialization succeeded).
- PostgreSQL connection pool exhausted.
HTTP 503 -- Service Unavailable
Health Check Failed
Endpoint: /health
{
"status": "red",
"message": "Health check failed",
"timestamp": "2026-01-15T12:00:00.000Z",
"error": "Error details here",
"response_time": "1500ms"
}
The health endpoint returns 503 when the overall system status is red. This
can happen if:
- The database is unreachable.
- The Mojang API has been consistently failing.
- The health check logic itself encounters an exception.
A yellow status (degraded performance) still returns HTTP 200.
Graceful Fallbacks
Not all failure scenarios produce an error response. Some are handled silently:
Bedrock Player with No Skin
When a Bedrock player has no custom skin (the GeyserMC API returns an empty
object), the API falls back to the Steve skin instead of returning an error:
if (Object.keys(skinResponse.data).length === 0) {
return getJavaProfile('Steve');
}
The client receives a valid PNG of Steve's head/body/avatar. There is no
indication in the response that a fallback occurred.
Bedrock API Unreachable
If the GeyserMC API request fails entirely (network error, timeout, etc.), the
same Steve fallback applies:
catch (error) {
return getJavaProfile('Steve');
}
Hat Layer Missing
In the createAvatarRender function, the hat layer extraction is wrapped in a
try/catch that silently ignores failures:
try {
const hat = skin.clone().crop(40, 8, 8, 8)
.resize(size, size, Jimp.RESIZE_NEAREST_NEIGHBOR);
avatar.composite(hat, 0, 0);
} catch (hatError) {
// Silently ignored -- render without hat
}
This handles rare cases where a skin texture is valid but the hat region is
malformed or the skin is too small to contain it.
Stats Recording Errors
The recordStats function catches and logs errors without propagating them:
async function recordStats(endpoint, input, edition) {
try {
// ... update count ...
} catch (err) {
console.error('Stats update error:', err);
}
}
A stats database failure will appear in server logs but will never cause a
rendering request to fail. The client is completely unaware of stats errors.
Summary Table
| Status | Error message | Endpoints |
|---|---|---|
| 400 | Direction must be "left" or "right" | /avatar, /ioshead, /iosbody |
| 500 | Failed to render head | /head |
| 500 | Failed to render player | /player |
| 500 | Failed to render avatar | /avatar |
| 500 | Failed to render iOS head | /ioshead |
| 500 | Failed to render iOS body | /iosbody |
| 500 | Failed to get skin | /skin |
| 500 | Failed to download skin | /download |
| 500 | Failed to get stats | /allstats |
| 500 | Failed to get bedrock stats | /allstatsbedrock |
| 500 | Failed to get sorted stats | /allstatsSorted |
| 503 | Health check failed | /health |