오류
모든 오류 코드의 의미와 재시도 여부.
Tahoe API의 모든 오류는 같은 형태로 반환됩니다. 분기 기준으로 쓸 수 있는 안정적인 code와, 코드를 계열별로 묶는 type이 들어 있습니다. 이 페이지에서는 모든 코드와 그 원인, 재시도가 도움이 되는지를 정리합니다.
오류 본문
오류는 detail 멤버 하나를 가진 JSON 객체입니다.
{
"detail": {
"code": "insufficient_scope",
"type": "permission",
"message": "This credential does not carry the contact:read scope.",
"param": null,
"doc_url": "https://tahoe.workonward.com/developers/errors#insufficient_scope",
"required_scope": "contact:read",
"retry_after_seconds": null
}
}| 필드 | 의미 |
|---|---|
code | 안정적인 식별자. 메시지가 아니라 이 값으로 분기하세요. |
type | 코드가 속한 계열. 같은 종류의 실패를 한꺼번에 처리할 때 씁니다. |
message | 사람이 읽기 위한 문장. 로그에 남겨도 되지만 파싱하면 안 됩니다. |
param | 문제가 된 쿼리 파라미터나 본문 필드. 없으면 null. |
doc_url | 이 문서에서 해당 코드 항목으로 가는 링크. |
required_scope | 스코프가 없어 403이 난 경우 필요한 스코프. 그 밖에는 null. |
retry_after_seconds | 429일 때 기다릴 시간. Retry-After 헤더와 같은 값입니다. |
errors | 유효성 검사가 실패한 경우에만 있습니다. 잘못된 필드마다 param과 message가 담긴 항목이 하나씩 들어갑니다. |
state, unlock_credits | resume_locked에만 있습니다. |
요청 ID는 본문에 없습니다. 성공 여부와 상관없이 모든 응답에 포함되는 Tahoe-Request-Id 헤더에 있습니다. 실패할 때마다 이 값을 로그에 남기고, 문의할 때 알려 주세요. 이 값이 있으면 해당 요청을 정확히 찾을 수 있습니다.
오류 유형
| 유형 | 상태 코드 | 대처 방법 |
|---|---|---|
invalid_request | 400, 413, 422 | 요청이 잘못되었습니다. 요청을 고치세요. 재시도해도 소용없습니다. |
authentication | 401 | 키를 사용할 수 없습니다. 키를 확인하세요. |
permission | 403 | 키는 유효하지만 이 작업이 허용되지 않았습니다. 스코프를 확인하세요. |
payment_required | 403 | 워크스페이스가 Tahoe에서 이 항목을 잠금 해제하지 않았습니다. 코드로 해결할 수 있는 문제가 아닙니다. |
not_found | 404 | 그런 객체가 없거나, 키로 접근할 수 없는 객체입니다. 둘은 일부러 똑같이 보입니다. |
conflict | 409 | 리소스가 변경되었거나, 이 작업을 허용하지 않는 상태이거나, 그 사람에게 연락할 수 없습니다. 재시도하기 전에 다시 읽으세요. |
rate_limit | 429 | 속도를 늦추세요. Retry-After만큼 기다리세요. |
unavailable | 429 | 요청하는 쪽의 잘못이 아닙니다. Retry-After만큼 기다린 뒤 재시도하세요. 보통 몇 초입니다. |
server | 500 | Tahoe 쪽 문제입니다. 한 번 재시도한 뒤, 요청 ID와 함께 알려 주세요. |
429가 항상 요청 한도 초과는 아닙니다
Tahoe가 잠시 요청을 처리할 수 없으면 type: "unavailable", 구체적인 code, Retry-After 헤더와 함께 429로 응답합니다. 503으로 응답하지 않습니다. 따라서 모든 429를 “너무 빨리 보내고 있다”는 뜻으로 받아들이지 마세요. type을 확인하세요. rate_limit은 속도를 늦추라는 뜻이고, unavailable은 요청 속도에는 문제가 없으니 몇 초 뒤 재시도하면 된다는 뜻입니다.
HTTP/1.1 429 Too Many Requests
Retry-After: 5
Tahoe-Request-Id: req_3f9a1c7e5b2d4a60
{
"detail": {
"code": "datastore_unavailable",
"type": "unavailable",
"message": "This resource is temporarily unavailable. Retry shortly.",
"param": null,
"doc_url": "https://tahoe.workonward.com/developers/errors#datastore_unavailable",
"required_scope": null,
"retry_after_seconds": 5
}
}400과 413: 요청 고치기
invalid_request
400 · 유형 invalid_request · 재시도: 아니요. 요청을 고치세요.
파라미터나 본문 필드가 없거나, 형식이 잘못되었거나, 서로 모순됩니다. param이 문제가 된 항목을 알려 줍니다. 여러 필드가 잘못되었으면 errors에 각각 나열됩니다. 유효성 검사 실패는 항상 이 형태의 400으로 반환되며, 422로 오는 일은 없습니다.
{
"detail": {
"code": "invalid_request",
"type": "invalid_request",
"message": "One or more parameters are invalid.",
"param": null,
"doc_url": "https://tahoe.workonward.com/developers/errors#invalid_request",
"required_scope": null,
"retry_after_seconds": null,
"errors": [
{ "param": "title", "message": "Field required" },
{ "param": "source.external_id", "message": "String should have at least 1 character" }
]
}
}invalid_cursor
400 · 유형 invalid_request · 재시도: 아니요. 첫 페이지부터 리스트를 다시 읽으세요.
커서 검증에 실패했거나, 다른 쿼리용으로 만들어졌거나, 만료되었습니다(커서는 1시간 동안 유효합니다). 흔한 원인은 반복 도중에 필터를 바꾸는 것입니다. 모든 페이지에 같은 필터를 보내고 cursor만 추가하세요. limit은 바꿔도 됩니다. 커서를 직접 만들거나, 디코딩하거나, 수정하지 마세요. 커서를 참고하세요.
invalid_timestamp
400 · 유형 invalid_request · 재시도: 아니요.
updated_after 같은 시간 필터가 유효한 RFC 3339 타임스탬프가 아닙니다. epoch 숫자나 yesterday 같은 단어가 아니라 2026-09-09T10:14:22.510Z 형식으로 보내세요.
workspace_id_required
400 · 유형 invalid_request · 재시도: 아니요. 파라미터를 추가하세요.
키가 둘 이상의 워크스페이스에 접근할 수 있으므로, 모든 요청에서 어느 워크스페이스인지 밝혀야 합니다. ?workspace_id=wsp_...를 추가하세요. 기본값도 “모든 워크스페이스”도 없습니다. 내 키가 여기에 해당하는지는 GET /me로 확인할 수 있습니다. 요청이 읽는 워크스페이스를 참고하세요.
unpublished_requires_opt_in
400 · 유형 invalid_request · 재시도: 아니요.
GET /jobs에 옵트인 없이, 초안처럼 published와 closed가 아닌 공고 상태를 요청했습니다. include_unpublished=true를 추가하세요. 이 옵트인은 /jobs를 그대로 가져가는 채용 공고 게시판이 리크루터가 작성 중인 초안을 실수로 보여 주는 일을 막기 위해 있습니다.
unknown_event_type
400 · 유형 invalid_request · 재시도: 아니요.
변경 피드의 ?type=에 넣은 이름이 이벤트 유형이 아닙니다. 알 수 없는 이름은 무시되지 않고 거부되므로, 오타 때문에 의존하던 이벤트가 아무 경고 없이 빠지는 일은 없습니다. 전체 목록은 변경 피드 페이지에 있습니다.
not_an_identity
400 · 유형 invalid_request · 재시도: 아니요.
GET /people/resolve에 보낸 값으로는 한 사람을 식별할 수 없습니다. 예를 들어 [email protected] 같은 공용 역할 주소는 그 주소를 쓰는 모든 사람을 한 사람으로 합치게 되므로 거부됩니다. 개인 이메일 주소나 LinkedIn 프로필 URL을 보내세요. Tahoe에 없는 사람이라면 대신 404 not_found가 반환됩니다.
result_window_exceeded
400 · 유형 invalid_request · 재시도: 아니요. 증분 동기화로 전환하세요.
한 리스트에서 10,000행보다 깊이 페이지를 넘겼습니다. 페이지 크기를 키워도 해결되지 않습니다. ?updated_after=로 마지막 동기화가 끝난 지점부터 읽거나, 변경 피드를 따라가세요.
unsupported_source_system
400 · 유형 invalid_request · 재시도: 아니요. source.system을 고친 뒤 다시 보내세요.
source.system은 직접 정하는 이름입니다. 영문 소문자, 숫자, 밑줄로 된 2~40자이며 예를 들면 acme_ats입니다. 이 코드는 값이 그 형식에 맞지 않거나, Tahoe가 자체용으로 예약한 이름이라는 뜻입니다. tahoe로 시작하는 이름과 몇몇 다른 이름은 항상 거부됩니다. 다른 이름을 고르세요.
invalid_idempotency_key
400 · 유형 invalid_request · 재시도: 아니요. 헤더를 고치세요.
Idempotency-Key 헤더가 A-Z a-z 0-9 . _ : ~ - 문자로 된 8~255자가 아닙니다. 중복 요청 방지를 참고하세요.
idempotency_key_required
400 · 유형 invalid_request · 재시도: 아니요. 헤더를 추가하세요.
이 엔드포인트는 재시도가 작업을 두 번 하지 않도록 Idempotency-Key 헤더를 요구합니다. 필수로 요구하는 호출은 POST /messages입니다.
invalid_stage
400 · 유형 invalid_request · 재시도: 아니요. 올바른 단계를 쓰세요.
단계를 쓸 수 없습니다. 지원서를 옮기거나 만들 때는 핸들 형식이 잘못되었거나, 단계가 없거나, 다른 공고나 워크스페이스의 단계이거나, (새 지원서의 경우) Hired나 Rejected 같은 마지막 단계입니다. 모두 같은 응답을 받습니다. 리스트 멤버의 단계를 정할 때는 그 리스트가 가질 수 없는 값이며, 메시지에 유효한 값이 나열됩니다.
invalid_event_type
400 · 유형 invalid_request · 재시도: 아니요. 이름을 고치세요.
웹훅 엔드포인트의 event_types에 있는 이름이 이벤트 카탈로그에 없습니다. param은 event_types입니다. 이벤트 유형을 참고하세요.
event_type_not_permitted
400 · 유형 invalid_request · 재시도: 아니요. 스코프가 있는 키를 쓰거나 그 유형을 빼세요.
웹훅 엔드포인트가 받도록 요청한 이벤트 유형의 읽기 스코프가 키에 없습니다. 메시지에 유형과 스코프가 표시됩니다.
webhook_url_not_allowed
400 · 유형 invalid_request · 재시도: 아니요. 공개 https URL을 쓰세요.
웹훅 엔드포인트의 URL이 규칙을 어기거나, 해석되지 않거나, 비공개 주소로 해석됩니다. param은 url입니다. URL 규칙을 참고하세요.
invalid_upload
400 · 유형 invalid_request · 재시도: 아니요. 요청을 고치세요.
POST /uploads가 그 파일 형식이나 크기로는 업로드를 예약할 수 없습니다.
unknown_answer_field
400 · 유형 invalid_request · 재시도: 아니요. 답변을 고치세요.
POST /applications의 답변이 공고의 지원서 양식에 없는 질문을 가리킵니다. param은 answers[i].field_id입니다.
duplicate_answer_field
400 · 유형 invalid_request · 재시도: 아니요. 질문마다 한 번만 답하세요.
POST /applications 하나에서 같은 질문에 두 번 답했습니다.
invalid_answer_value
400 · 유형 invalid_request · 재시도: 아니요. 값을 고치세요.
답변의 형태가 틀렸거나 질문의 선택지에 없는 값입니다.
answer_field_not_accepted
400 · 유형 invalid_request · 재시도: 아니요. 그 질문을 빼세요.
평등 고용 기회 질문, 동의 체크박스, 섹션 제목, 이력서 필드입니다. 앞의 둘은 후보자만 답할 수 있으며 API는 받지 않습니다.
answer_field_reserved
400 · 유형 invalid_request · 재시도: 아니요. candidate에 넣어 보내세요.
이름, 이메일, 전화번호, LinkedIn 필드입니다. 이 값은 답변이 아니라 candidate 객체에 넣어 보내세요.
missing_required_answers
400 · 유형 invalid_request · 재시도: 아니요. 답변을 추가하세요.
공고 양식의 필수 질문에 답이 없습니다. 메시지에는 질문이 표시되며 후보자 정보는 나오지 않습니다. 필수 전화번호와 이력서는 세지 않습니다.
upload_not_found
400 · 유형 invalid_request · 재시도: 아니요. 파일을 다시 올리세요.
resume_upload_id가 없거나, 만료되었거나, 다른 워크스페이스의 것입니다. 세 경우는 일부러 똑같이 보입니다.
resume_rejected
400 · 유형 invalid_request · 재시도: 아니요. 올바른 파일을 올리세요.
올린 파일이 비어 있거나 5MB를 넘습니다. 파일은 삭제되고 업로드 ID는 소모됩니다.
payload_too_large
413 · 유형 invalid_request · 재시도: 아니요. 더 작은 본문을 보내세요.
요청 본문이 256KB보다 큽니다. 본문을 읽기 전에, 선언된 크기만 보고 거부합니다. 본문을 받는 엔드포인트에는 항목 수 제한도 있습니다. 예를 들어 POST /people/resolve:batch는 호출당 최대 100건까지 조회합니다.
401과 403: 키와 권한
unauthenticated
401 · 유형 authentication · 재시도: 아니요. 다른 키로 바꾸는 경우만 예외입니다.
키와 관련된 모든 문제에 이 코드 하나를 씁니다. 헤더가 없는 경우, 헤더를 읽을 수 없는 경우, 알 수 없는 키, 폐기되었거나 만료된 키, 키의 IP 허용 목록 밖에서 온 요청이 모두 해당합니다. 탈취한 키를 가진 사람이 그 키가 아직 작동하는지 시험할 수 없도록 일부러 똑같이 응답합니다. 401을 반복해서 재시도하지 마세요. 모든 키 문제에 같은 401을 참고하세요.
insufficient_scope
403 · 유형 permission · 재시도: 아니요. 해당 스코프가 있는 키를 Tahoe에 요청하세요.
키는 유효하지만 이 엔드포인트에 필요한 스코프가 없습니다. required_scope에 필요한 스코프가 나옵니다. 기존 키에는 스코프를 추가할 수 없으므로 새 키가 필요합니다.
필드 하나에 대한 스코프가 없을 때는 403이 아닙니다. 요청은 성공하고 해당 필드가 restricted에 나열됩니다. 가려진 필드를 참고하세요.
write_requires_api_key
403 · 유형 permission · 재시도: 아니요.
쓰기는 모두 Tahoe 사용자가 만든 API 키가 필요합니다. 쓰기는 그 사람이 한 것으로 기록되기 때문입니다. Sign in with Tahoe 토큰은 한 사람을 대신해 동작하며 읽을 수 있습니다. 연결된 앱의 쓰기가 켜져 있을 때만 쓸 수 있습니다. Tahoe가 Tahoe 사용자와 연결할 수 없는 키도 같은 코드를 받습니다. 설정의 개발자 탭에서 새 키를 만드세요.
webhooks_self_serve_disabled
403 · 유형 permission · 재시도: 아니요. 문의하거나 켜질 때까지 기다리세요.
API로 웹훅 엔드포인트를 등록하는 기능이 아직 켜져 있지 않습니다. [email protected]으로 문의하면 Tahoe가 엔드포인트를 대신 등록해 드립니다.
resume_locked
403 · 유형 payment_required · 재시도: 아니요. 워크스페이스가 Tahoe에서 이력서를 잠금 해제해야 합니다.
이력서는 존재하지만, 지금은 워크스페이스가 열람하거나 다운로드할 수 없습니다. API는 제품과 같은 이력서 열람 기간을 따릅니다. 지원 후 90일 동안은 무료로 열람하고 다운로드할 수 있고, 120일째까지는 열람만 할 수 있으며(파일 다운로드 불가), 그 뒤에는 워크스페이스가 잠금 해제할 때까지 잠깁니다. 잠금 해제에는 한 번 50크레딧이 들고, 효과는 영구적입니다. API로 제품보다 저렴하게 이력서를 얻는 방법은 없습니다.
본문에 state와 잠금 해제 비용이 들어 있으므로 사용자에게 알려 줄 수 있습니다. 열람 기간은 시간에 따라 바뀌므로, 지난달에 읽을 수 있던 이력서가 내 쪽에서 아무것도 바뀌지 않았는데도 오늘은 잠겨 있을 수 있습니다.
{
"detail": {
"code": "resume_locked",
"type": "payment_required",
"message": "This resume is not currently viewable by the workspace that owns it.",
"param": null,
"doc_url": "https://tahoe.workonward.com/developers/errors#resume_locked",
"required_scope": null,
"retry_after_seconds": null,
"state": "locked",
"unlock_credits": 50
}
}404: 찾을 수 없음
not_found
404 · 유형 not_found · 재시도: 아니요.
그런 객체가 없거나, 존재하지만 키로 접근할 수 없습니다. 둘은 일부러 똑같이 보입니다. 읽을 수 없는 워크스페이스에 레코드가 있다고 확인해 주는 것만으로도 정보가 새어 나가기 때문입니다. 형식이 잘못되었거나 유형이 다른 ID도 404입니다.
409: 충돌
job_modified_concurrently
409 · 유형 conflict · 재시도: 예. 채용 공고를 다시 가져온 뒤 다시 보내세요.
POST /jobs가 저장되는 동안 공고가 변경되었습니다. 보통 같은 순간에 리크루터가 Tahoe에서 공고를 수정하고 있었던 경우입니다. 보낸 본문에는 문제가 없습니다. 공고를 다시 가져온 뒤 본문을 그대로 다시 보내세요.
import_link_conflict
409 · 유형 conflict · 재시도: 아니요. Tahoe에 연결 검토를 요청하세요.
회사는 인증된 소유자 한 명이 Tahoe 워크스페이스 하나에만 연결할 수 있습니다. 요청한 연결이 이미 기록된 연결과 일치하지 않아, 담당자가 검토하기 전까지 거부되었습니다. Tahoe가 파트너와 함께 설정하는 계정 연결 호출에만 해당합니다.
invalid_job_transition
409 · 유형 conflict · 재시도: 공고 상태가 바뀌기 전까지는 아니요.
공고의 현재 상태에서는 요청한 일을 할 수 없습니다. POST /jobs에서는 그렇게 옮길 수 없는 공고에 publish: true나 status: "closed"를 보낸 경우이며, 공고 자체는 생성되거나 수정되었고 게시나 마감만 거부되었습니다. 라이프사이클 호출에서는 공고의 현재 상태에서 그 전환이 허용되지 않는 경우입니다. 이미 게시된 공고를 게시하는 것이 그 예입니다.
stale_resource
409 · 유형 conflict · 재시도: 예. 리소스를 다시 읽은 뒤에.
expected_updated_at을 보냈는데 그 사이 지원서나 공고가 바뀌었습니다. 아무것도 바뀌지 않았습니다. 리소스를 다시 읽고 변경이 아직 유효한지 판단하세요.
application_modified_concurrently
409 · 유형 conflict · 재시도: 예.
잠금과 쓰기 사이에 지원서가 바뀌었습니다. 보통은 일어나지 않습니다. 다시 시도하세요.
invalid_application_state
409 · 유형 conflict · 재시도: 상태가 바뀌기 전까지는 아니요.
탈락 처리는 이미 rejected, withdrawn, hired가 아닌 지원서에만 할 수 있습니다. 탈락 취소는 탈락 상태의 지원서에만 할 수 있습니다. 지원서를 읽어 상태부터 확인하세요.
idempotency_key_reused
409 · 유형 conflict · 재시도: 아니요. 새 키를 쓰세요.
같은 Idempotency-Key를 이미 다른 요청에 썼습니다. 새 요청에는 새 키를 쓰세요. 중복 요청 방지를 참고하세요.
idempotency_in_flight
409 · 유형 conflict · 재시도: 예. 잠시 기다린 뒤에.
같은 Idempotency-Key의 요청이 아직 실행 중입니다. 잠시 후 같은 키로 다시 보내면 그 응답을 받습니다.
recipient_unsubscribed
409 · 유형 conflict · 재시도: 아니요.
수신 거부했거나, 처리가 중단되었거나, 삭제되었거나, 메일을 받을 수 없는 주소라서 POST /messages가 보내지 않았습니다. 다시 시도하지 마세요.
sender_email_missing
409 · 유형 conflict · 재시도: 사용자에게 이메일 주소가 생기기 전까지는 아니요.
키가 대신하는 Tahoe 사용자에게 이메일 주소가 없어 답장이 갈 곳이 없습니다. 아무것도 보내지 않았습니다.
job_not_accepting_applications
409 · 유형 conflict · 재시도: 공고가 지원을 받기 전까지는 아니요.
POST /applications는 게시 중이고 지원을 받는 공고에만 쓸 수 있습니다. 초안, 예약, 일시 중지, 마감된 공고는 받지 않습니다.
applicant_suppressed
409 · 유형 conflict · 재시도: 아니요. 이 사람을 더 보내지 마세요.
그 사람이 삭제되었거나 Tahoe에 데이터 처리 중단을 요청했습니다. 지원서를 만들지 않았습니다. 삭제 통지를 참고하세요.
applicant_unsubscribed
409 · 유형 conflict · 재시도: 아니요.
그 사람이 이 워크스페이스에서 수신 거부해서 새 지원서를 만들지 않았습니다. 이미 그 공고에 있는 사람은 그대로 200으로 반환됩니다.
candidate_consent_required
409 · 유형 conflict · 재시도: 아니요. 후보자가 직접 지원해야 합니다.
공고 양식에 후보자만 체크할 수 있는 필수 동의 항목이 있습니다. 메시지에 그 항목이 표시됩니다.
upload_already_used
409 · 유형 conflict · 재시도: 아니요. 파일을 다시 올리세요.
그 업로드는 이미 지원서에 연결되었습니다. 업로드는 한 번만 쓸 수 있습니다.
upload_not_completed
409 · 유형 conflict · 재시도: 예. 파일을 올린 뒤에.
업로드 URL에 아직 파일이 도착하지 않았습니다. 그곳에 파일을 PUT한 뒤 같은 업로드 ID로 다시 시도하세요.
application_conflict
409 · 유형 conflict · 재시도: 예.
확인과 삽입 사이에 동시 요청이 같은 지원서를 만들었고, 그것을 다시 읽을 수 없었습니다. 다시 시도하면 그 지원서를 받습니다.
webhook_endpoint_exists
409 · 유형 conflict · 재시도: 아니요. 기존 엔드포인트를 바꾸거나 켜세요.
이 키가 이 워크스페이스에 같은 URL의 엔드포인트를 이미 가지고 있습니다.
webhook_endpoint_limit_reached
409 · 유형 conflict · 재시도: 엔드포인트를 끄기 전까지는 아니요.
키의 활성 엔드포인트가 10개이거나, 워크스페이스의 활성 엔드포인트가 20개이거나, 키의 엔드포인트가 비활성 포함 100개입니다.
webhook_endpoint_disabled
409 · 유형 conflict · 재시도: 엔드포인트를 켜기 전까지는 아니요.
꺼져 있는 엔드포인트에 테스트 이벤트를 요청했습니다.
422: 공고가 아직 준비되지 않았습니다
job_not_publishable
422 · 유형 invalid_request · 재시도: 아니요. 공고를 완성한 뒤 게시하세요.
공개 페이지에 필요한 내용이 없어서 POST /jobs/{job_handle}/publish가 거부되었습니다. reasons에 빠진 항목이 나옵니다. 제목이 비어 있으면 title, 설명과 요약 모두에 텍스트가 없으면 description입니다.
429: 기다린 뒤 재시도
rate_limit_exceeded
429 · 유형 rate_limit · 재시도: 예. Retry-After만큼 기다린 뒤에.
이 엔드포인트의 분당 한도를 넘었습니다. Retry-After에 나온 초만큼 기다리세요. 바로 재시도하거나 짧은 간격으로 반복하지 마세요. 요청 한도와 할당량을 참고하세요.
message_quota_exceeded
429 · 유형 rate_limit · 재시도: 예. Retry-After만큼 기다린 뒤에.
키가 POST /messages로 하루 최대치의 메시지를 보냈습니다. 기본값은 UTC 하루 200통입니다. Retry-After는 UTC 자정까지 남은 초입니다. 실패한 요청은 한도를 소모하지 않습니다.
too_many_pending_uploads
429 · 유형 rate_limit · 재시도: 예. Retry-After만큼 기다린 뒤에.
워크스페이스에 지원서에 연결되지 않았고 만료되지도 않은 이력서 업로드가 500개 이상 있습니다. Retry-After는 600초입니다.
webhook_test_throttled
429 · 유형 rate_limit · 재시도: 예. Retry-After만큼 기다린 뒤에.
방금 이 엔드포인트로 테스트 이벤트를 보냈습니다. 엔드포인트당 10초에 한 번만 테스트할 수 있습니다.
personal_data_quota_exceeded
429 · 유형 rate_limit · 재시도: 오늘은 아니요. 하루 단위 한도입니다.
키가 개인 데이터를 반환하는 읽기의 일일 한도를 모두 썼습니다. 이 한도는 요청 한도와 별개입니다. 채용 공고를 페이지로 읽는 것은 이 한도를 쓰지 않지만, 전화번호를 읽는 것은 씁니다. 한도는 UTC 자정에 초기화되며 Retry-After도 그때까지 남은 시간을 세므로, 몇 시간이 될 수도 있습니다. 계획된 백필 도중에 한도에 걸렸다면 재시도로 밀어붙이지 말고 문의해 주세요.
partner_api_disabled
429 · 유형 unavailable · 재시도: 아니요. 재시도해도 응답은 바뀌지 않습니다.
Tahoe API가 꺼져 있습니다. 일시적인 장애가 아니며, Tahoe가 다시 켤 때까지 모든 요청이 같은 응답을 받습니다. 문의해 주세요.
limiter_unavailable
429 · 유형 unavailable · 재시도: 예. Retry-After만큼 기다린 뒤에.
Tahoe가 잠시 이 요청을 요청 한도에 반영할 수 없었습니다. 그래서 한도에 반영하지 않은 채 처리하는 대신 요청을 거부했습니다. 요청 속도에는 문제가 없습니다.
datastore_unavailable
429 · 유형 unavailable · 재시도: 예. Retry-After만큼 기다린 뒤에.
이 요청에 필요한 데이터에 잠시 접근할 수 없었습니다. 아무것도 반환되지 않았으므로 안전하게 재시도할 수 있습니다.
quota_unavailable
429 · 유형 unavailable · 재시도: 예. Retry-After만큼 기다린 뒤에.
이 읽기는 개인 데이터를 반환하는데, Tahoe가 키의 일일 한도를 확인할 수 없었습니다. 한도를 확인하지 않은 채 통과시키는 대신 읽기를 거부했습니다.
audit_unavailable
429 · 유형 unavailable · 재시도: 예. Retry-After만큼 기다린 뒤에.
이 읽기는 개인 데이터를 반환하는데, Tahoe가 제공 기록을 남길 수 없어 읽기를 거부했습니다. 아무것도 제공되지 않았습니다. 기록 없이 누군가의 연락처를 내주는 것은 요청 실패보다 나쁘고, 재시도에 드는 비용은 아주 작습니다.
pool_search_unavailable
429 · 유형 unavailable · 재시도: 예. Retry-After만큼 기다린 뒤에.
공유 인재풀 검색(POST /pool/search)을 잠시 사용할 수 없었습니다. 인재풀 프로필을 하나씩 읽는 요청에는 영향이 없습니다.
suppression_check_unavailable
429 · 유형 unavailable · 재시도: 예. Retry-After만큼 기다린 뒤에.
Tahoe가 처리 중단 목록을 읽지 못해서 POST /applications가 아무것도 만들지 않았습니다. 잠시 후 다시 시도하세요.
webhook_secrets_unavailable
429 · 유형 unavailable · 재시도: 예. Retry-After만큼 기다린 뒤에.
Tahoe가 지금은 웹훅 서명 시크릿을 만들거나 읽을 수 없습니다. 아무것도 바뀌지 않았습니다.
webhooks_delivery_disabled
429 · 유형 unavailable · 재시도: 아니요. 전송이 켜지기 전까지는.
웹훅 전송이 꺼져 있어 테스트 이벤트가 전송되지 않습니다. 엔드포인트 생성은 그대로 됩니다.
delivery_failed
429 · 유형 unavailable · 재시도: 예. 새 Idempotency-Key로.
메일 제공자가 POST /messages의 메시지 발송을 거부했거나 실패했습니다. 상대에게 아무것도 가지 않았습니다. 같은 키는 같은 실패를 다시 알려 주며, 실패한 발송이 실수로 반복되지 않도록 한 설계입니다. 새 키로 다시 시도하세요.
500: Tahoe 쪽 문제
internal_error
500 · 유형 server · 재시도: 한 번만. 그다음에는 알려 주세요.
Tahoe 쪽에서 문제가 발생했습니다. 본문에는 의도적으로 더 이상의 정보가 없습니다. 내부 오류의 세부 내용은 Tahoe 로그에만 남습니다. 500의 본문에 의존하지 마세요. Tahoe-Request-Id 헤더에서 ID를 읽어 보내 주세요.
재시도 기준
429와 5xx는 재시도하되, Retry-After가 있으면 그만큼 기다리세요. 단, partner_api_disabled는 저절로 풀리지 않으니 멈추고, personal_data_quota_exceeded는 다음 날 다시 시작하세요. 409 job_modified_concurrently, 409 stale_resource, 409 application_modified_concurrently는 리소스를 다시 읽은 뒤에, 그리고 409 idempotency_in_flight는 같은 키로 잠시 기다린 뒤에 재시도하세요. 400, 401, 403, 404, 413, 422는 절대 재시도하지 마세요. 응답은 바뀌지 않으며, 401이 반복되는 모습은 누군가 키를 추측하는 것과 똑같아 보입니다. 쓰기는 같은 Idempotency-Key로만 재시도해, 재시도가 작업을 두 번 하지 않게 하세요. 예외는 새 키가 필요한 delivery_failed입니다.
import time
def call(session, method, url, *, attempts=4, **kwargs):
"""Send a request, retrying only what is worth retrying."""
for attempt in range(attempts):
response = session.request(method, url, timeout=30, **kwargs)
code = response.json().get("detail", {}).get("code") if response.status_code == 429 else None
retryable = response.status_code >= 500 or (
response.status_code == 429 and code not in ("partner_api_disabled", "personal_data_quota_exceeded")
)
if not retryable or attempt == attempts - 1:
return response
# Use the server's own number when it sends one, and give up on a
# wait longer than five minutes rather than sleeping through it.
wait = response.headers.get("Retry-After")
delay = int(wait) if wait and wait.isdigit() else 2 ** attempt
if delay > 300:
return response
time.sleep(delay)
return response