invalid_cursor adalah kode error HTTP 400 pada Se-Hari API. Nilai starting_after yang Anda kirim bukan cursor yang pernah kami keluarkan. Cursor bersifat buram: ia menyandikan posisi baris terakhir dan tidak dimaksudkan untuk dibaca, dibuat, atau dimodifikasi klien.
#Bentuk responsnya
Semua error Se-Hari memakai amplop yang sama. Yang perlu dicabang di kode Anda adalah code, bukan message — teks pesan bisa kami perbaiki sewaktu-waktu, sedangkan code adalah kontrak publik yang hanya berubah lewat versi API baru.
{
"error": {
"code": "invalid_cursor",
"message": "Contoh pesan untuk invalid_cursor.",
"docs_url": "https://se-hari.com/docs/error/invalid-cursor",
"request_id": "req_a1b2c3d4e5f6",
"details": {}
}
}#Kapan ini muncul
Kode ini hanya muncul pada endpoint daftar, dan hampir selalu berarti kode paginasi ditulis dengan asumsi offset. Gejala yang menyertainya sering lebih membingungkan daripada errornya sendiri: sebagian penelusuran berhasil, lalu berhenti di tengah dengan 400 — karena halaman pertama tidak butuh cursor sama sekali, sehingga bug-nya baru terlihat pada halaman kedua.
#Kenapa ini terjadi
- Cursor dibuat sendiri, misalnya diisi id baris terakhir
- Cursor terpotong saat disimpan ke kolom database yang terlalu pendek
- Cursor di-URL-encode dua kali oleh HTTP client
- Cursor dari endpoint lain dipakai di endpoint ini — setiap daftar punya ruang cursor sendiri
#Cara memperbaikinya
Kirim kembali persis nilai next_cursor dari respons sebelumnya, tanpa perubahan apa pun. Kalau Anda menyimpannya, pastikan kolomnya cukup panjang; cursor bisa melebihi 60 karakter.
#Yang memicu error
# SALAH: memakai id baris terakhir sebagai cursor
curl "https://se-hari.com/api/v1/notes?starting_after=c0ffee00-1111-2222-3333-444455556666" \
-H "Authorization: Bearer $SEHARI_API_KEY"#Yang seharusnya
# BENAR: memakai next_cursor apa adanya
CURSOR=$(curl -s "https://se-hari.com/api/v1/notes?limit=100" \
-H "Authorization: Bearer $SEHARI_API_KEY" | jq -r '.next_cursor')
curl "https://se-hari.com/api/v1/notes?limit=100&starting_after=$CURSOR" \
-H "Authorization: Bearer $SEHARI_API_KEY"#Aman diulang?
Percuma tanpa perubahan. Ambil ulang halaman pertama untuk mendapatkan cursor yang sah, lalu lanjutkan dari sana. Mengulang dengan cursor yang sama hanya menghasilkan error yang sama.
#Jangan tertukar dengan
Ini bukan not_found. Cursor yang menunjuk baris yang sudah terhapus tetap valid dan tetap dijawab dengan halaman berikutnya; yang ditolak di sini adalah cursor yang bentuknya tidak kami kenali sama sekali. Kalau daftar Anda kembali kosong tanpa error, itu bukan kode ini melainkan tanda bahwa filternya memang tidak cocok dengan apa pun.
#Mencegahnya terulang
Perlakukan cursor sebagai token buram. Jangan menyimpannya di kolom varchar(32), jangan memotongnya untuk log, dan jangan pernah membangunnya sendiri. Kalau Anda perlu melanjutkan penelusuran di proses lain, simpan seluruh nilainya apa adanya. Untuk pekerjaan besar, pola paling aman adalah menyelesaikan satu penelusuran penuh dalam satu proses, karena cursor menggambarkan posisi dalam daftar yang isinya bisa bertambah di antara dua sesi.
#Menangani ini di kode
Cabangkan pada error.code, dan bedakan kegagalan yang layak diulang dari yang tidak. Mengulang kegagalan yang tidak akan pernah berhasil hanya menghabiskan kuota rate limit — dan menyembunyikan kegagalan yang sebenarnya butuh perhatian Anda.
const res = await fetch('https://se-hari.com/api/v1/me', {
headers: { Authorization: `Bearer ${process.env.SEHARI_API_KEY}` }
});
if (!res.ok) {
const { error } = await res.json();
if (error.code === 'invalid_cursor') {
// Kirim kembali persis nilai `next_cursor` dari respons sebelumnya, tanpa perubahan apa…
console.error(error.message, error.details);
}
// request_id adalah satu-satunya cara kami menemukan request ini di log.
console.error(`request_id: ${error.request_id}`);
}#Selanjutnya
- Semua kode error — daftar lengkap
- Autentikasi — scope dan siklus hidup API key
- Referensi API — error apa saja yang mungkin muncul di tiap endpoint