Skip to content

Errors API

Report errors to MiniAPM for tracking, grouping, and analysis.

POST /ingest/errors
HeaderRequiredDescription
AuthorizationYesBearer <api_key>
Content-TypeYesapplication/json
{
"exception_class": "RuntimeError",
"message": "Something went wrong",
"backtrace": [
"app/models/user.rb:42:in `save`",
"app/controllers/users_controller.rb:15:in `create`"
],
"fingerprint": "user_save_runtime_error",
"request_id": "abc-123-def",
"user_id": "123",
"params": { "id": "999" },
"timestamp": "2024-01-01T12:00:00Z"
}
FieldTypeRequiredDescription
exception_classstringYesException class name
messagestringYesError message
backtracearrayYesStack trace lines
fingerprintstringYesGrouping key for deduplication
request_idstringNoRequest identifier
user_idstringNoUser identifier
paramsobjectNoAdditional context data (JSON)
timestampstringNoISO 8601 timestamp (defaults to now)
source_contextobjectNoSource code context (see below)

Returns 202 Accepted on success with no response body.

Terminal window
curl -X POST http://localhost:3000/ingest/errors \
-H "Authorization: Bearer proj_abc123..." \
-H "Content-Type: application/json" \
-d '{
"exception_class": "ActiveRecord::RecordNotFound",
"message": "Could not find User with id=999",
"backtrace": [
"app/controllers/users_controller.rb:10:in `show`",
"lib/middleware/auth.rb:25:in `call`"
],
"fingerprint": "users_controller_record_not_found",
"params": { "id": "999" }
}'

Report multiple errors in a single request.

POST /ingest/errors/batch
{
"errors": [
{
"exception_class": "RuntimeError",
"message": "Error 1",
"backtrace": ["..."],
"fingerprint": "fp1"
},
{
"exception_class": "NoMethodError",
"message": "Error 2",
"backtrace": ["..."],
"fingerprint": "fp2"
}
]
}

Returns 202 Accepted on success. Returns 500 only if all errors fail to ingest.

MiniAPM groups errors by fingerprint. The client is responsible for computing the fingerprint, typically from:

  1. Exception class name
  2. Error location (top stack frame)
  3. Normalized message

Errors with the same fingerprint are grouped together, showing:

  • First seen timestamp
  • Last seen timestamp
  • Occurrence count
  • Trend over time

Include source code context for richer error display in the dashboard:

{
"exception_class": "RuntimeError",
"message": "Invalid state",
"backtrace": ["app/models/user.rb:42:in `save`"],
"fingerprint": "user_save_invalid",
"source_context": {
"file": "app/models/user.rb",
"lineno": 42,
"pre_context": [" def save", " validate!"],
"context_line": " raise 'Invalid' unless valid?",
"post_context": [" end", ""]
}
}
FieldTypeRequiredDescription
filestringYesFile path
linenointegerYesLine number
context_linestringYesThe error line
pre_contextarrayNoLines before the error
post_contextarrayNoLines after the error

Errors can be managed through the dashboard:

  • Open - New or active error
  • Resolved - Fixed, but can reopen if it recurs
  • Ignored - Won’t show in dashboard
begin
risky_operation
rescue => e
MiniAPM.record_error(e, context: {
user_id: current_user.id,
params: request.params.except(:password)
})
raise
end

The miniapm gem captures exceptions automatically in Rails.

import requests
def report_error(exception, context=None):
import traceback
requests.post(
'http://localhost:3000/ingest/errors',
headers={
'Authorization': 'Bearer proj_abc123...',
'Content-Type': 'application/json'
},
json={
'exception_class': type(exception).__name__,
'message': str(exception),
'backtrace': traceback.format_exception(exception),
'fingerprint': f"{type(exception).__name__}_{traceback.extract_tb(exception.__traceback__)[-1].filename}",
'params': context or {}
}
)
async function reportError(error, context = {}) {
await fetch('http://localhost:3000/ingest/errors', {
method: 'POST',
headers: {
'Authorization': 'Bearer proj_abc123...',
'Content-Type': 'application/json'
},
body: JSON.stringify({
exception_class: error.name,
message: error.message,
backtrace: error.stack?.split('\n') || [],
fingerprint: `${error.name}_${error.message}`,
params: context
})
});
}