[fix] solution/backend: 플랫폼 거절에 사유를 붙인다 — THREADS_REJECTED_400 만으로는 못 고친다

연동이 실패했는데 로그에 `THREADS_REJECTED_400` 만 남았다. 코드가 만료됐는지, 리디렉션
URI 가 안 맞는지, 권한이 모자란지 구별이 안 돼 원인을 세 번 헛짚었다(실측 2026-09-18).
Meta 는 응답 본문에 `error.message` 와 `error_subcode` 로 이유를 정확히 말해 주는데,
우리가 그걸 읽고 버리고 있었다.

- external/threads._read: 거절 코드 뒤에 `[subcode] message` 를 붙인다
- ★ 담는 것은 message·subcode 뿐이다. 토큰·시크릿·인가 code 는 담지 않는다 —
  이 문자열은 로그로 가고 로그는 우리가 아닌 사람도 본다. message 는 160자에서 끊는다

검증: SNS 테스트 14건 통과. 실제 호출로 엔드포인트·자격증명이 정상임을 먼저 확인했다
(더미 code 로 `Invalid verification code` 응답 · debug_token·장기토큰 교환 호출 모양 정상)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hbyang 2026-09-18 13:26:38 +09:00
parent 176a77a231
commit 6badd5c9f0

View File

@ -42,8 +42,18 @@ def _read(res):
raise SocialError("THREADS_INVALID_RESPONSE") from ex
if res.status_code >= 400 or data.get("error"):
error = data.get("error") or {}
# ★ 플랫폼이 준 사유를 코드에 붙인다. `THREADS_REJECTED_400` 만으로는 무엇이 틀렸는지
# 알 수 없어서 — 코드가 만료됐는지, 리디렉션 URI 가 안 맞는지, 권한이 모자란지 —
# 실제로 원인을 좁히지 못했다(실측 2026-09-18: 연결 실패가 400 이라는 것만 알고
# Meta 가 뭐라고 했는지는 어디에도 안 남아 세 번을 헛짚었다).
# ★ 남기는 것은 message·subcode 뿐이다. 토큰·시크릿·code 는 담지 않는다 —
# 이 문자열은 로그로 가고, 로그는 우리가 아닌 사람도 본다.
detail = str(error.get("message") or "")[:160]
subcode = error.get("error_subcode") or error.get("code")
raise SocialError(
f"THREADS_REJECTED_{res.status_code}",
f"THREADS_REJECTED_{res.status_code}"
+ (f"[{subcode}]" if subcode else "")
+ (f" {detail}" if detail else ""),
reauth=error.get("code") == 190 or res.status_code == 401,
)
return data