Cupix::Errors::PermissionDenied: Archived entity
RCA: Cupix::Errors::PermissionDenied: Archived entity
Overview#
What Happened#
cupixworks-api에서 archived 상태의 parent entity 아래에 하위 리소스를 생성(POST .../create)하려는 요청이 Cupix::Errors::PermissionDenied (PERM32000, "Archived entity")로 거부되었다. 이는 base_factory.rb의 check_archived_entity 권한 가드가 의도적으로 발생시키는 HTTP 403 응답으로, 모든 Datadog 샘플이 status: info로 기록된다. 클러스터의 Representative Error("Archived entity", 2025-02-07 최초 발생)와 최근 발생 로그(2026-08-01)의 메시지·코드가 동일하여 stale 이슈가 아니다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Cupix::Errors::PermissionDenied |
| exception.message | Archived entity |
| error.code | PERM32000 |
| top_frame | app/factories/base_factory.rb:151 |
| http_status | 403 |
| env | production (cupixworks-api) |
Affected Teams#
에러 로그는 client별 team 도메인을 노출하지 않으므로 팀 단위 영향 범위는 특정할 수 없다. 대신 영향받은 엔드포인트 기준으로 정리한다.
| Endpoint | Error Count (14d sample) | Impact |
|---|---|---|
POST /api/v1/videos |
다수 | archived facility/parent에 video 생성 시도 → 403 |
POST /api/v1/buildings |
다수 | archived parent에 building 생성 시도 → 403 |
POST /api/v1/facilities/{id}/mounts |
소수 | archived facility에 mount 생성 시도 → 403 |
POST /api/v1/reviews |
소수 | archived parent에 review 생성 시도 → 403 |
전체 occurrence_count는 최초 발생 이후 약 18개월간 21건으로, 매우 낮은 빈도다.
Timeline#
- 2025-02-07 14:55 KST — 최초 발생 (first_seen, Error Tracking 등록)
- 2026-07-30 09:10~09:44 KST —
POST /api/v1/videos에서 다수 403 발생 - 2026-08-01 18:47~18:53 KST —
POST /api/v1/buildings,/mounts에서 403 다발 - 2026-08-02 00:23 KST — last_seen (
POST /api/v1/videos) - 2026-08-04 KST — RCA 수행
Error Log#
Archived entity
최근 발생 로그(last_seen 부근)의 원문:
{
"timestamp": "2026-08-02 00:23:58",
"status": "info",
"message": "[403] POST /api/v1/videos (Api::V1::VideosController#create)",
"error": {
"reason": "Archived entity",
"code": "PERM32000",
"message": "Archived entity",
"class": "Cupix::Errors::PermissionDenied"
}
}
Impact#
- Service:
cupixworks-api - 발생 횟수: 21
- 최초 발생: 2025-02-07 14:55 KST
- 최근 발생: 2026-08-02 00:23 KST
Root Cause Summary#
이 에러는 코드 버그가 아니라 의도된 권한 가드가 발생시키는 정상적인 HTTP 403 응답이다. BaseFactory#create!는 하위 엔티티 생성 전에 check_archived_entity를 호출하는데, parent entity의 applied_cycle_state가 archived 또는 archiving이고 요청 사용자가 해당 parent에 대한 admin(create? policy) 권한이 없으면 Cupix::Errors::PermissionDenied(code: 'PERM32000')를 raise한다. 즉 사용자가 이미 아카이브된 facility/building 등의 parent 아래에 video/mount/review 같은 하위 리소스를 추가하려 할 때 정책상 거부되는 것으로, 클라이언트 입력/권한 검증 실패에 해당한다. 모든 Datadog 샘플이 status: info(403)로 기록되어 error/warn 레벨이 아니며, 18개월간 21건으로 빈도도 매우 낮다.
Technical Analysis#
Code Path#
- Entry point: 각
Api::V1::*Controller#create→ factorycreate!호출 - Guard:
app/factories/base_factory.rb:83—check_archived_entity호출 - Failure point:
app/factories/base_factory.rb:151— PERM32000 raise
create!는 하위 엔티티를 저장하기 전에 archived 가드를 먼저 실행한다.
def create!(params = {})
raise Cupix::Errors::Unauthorized.new(code: 'ARG10000', reason: 'current_user is required') if self.current_user.nil? && current_user_required?
check_archived_entity
check_updatable_by_billing_state!
if self.current_user.present? && (self.parent.present? || self.review.present?)
raise Cupix::Errors::PermissionDenied.new(code: 'PERM10000', reason: 'Permission denied') if !(Pundit.policy(self.current_user, self.parent).create? rescue false) && !(Pundit.policy(self.current_user, self.review).create? rescue false)
end
가드 본체는 parent가 applied_cycle_state에 응답하고, 그 값이 archived/archiving이며, 사용자가 admin이 아닐 때 403을 발생시킨다.
def check_archived_entity
if self.current_user.present? && self.parent.present? && self.parent.respond_to?(:applied_cycle_state)
raise Cupix::Errors::PermissionDenied.new(code: 'PERM32000', reason: 'Archived entity') if %w[archived archiving].include?(self.parent.applied_cycle_state) && !Pundit.policy(self.current_user, self.parent).create?
end
end
에러 코드 정의상 이 응답의 의미는 "아카이브된 엔티티는 관리자만 수정할 수 있음"이다.
PERM32000:
message: "Permission denied: Only administrator can modify archived entities"
기대 동작 vs 실제 동작: 기대 동작 그대로다 — 비관리자 사용자가 아카이브된 parent에 하위 리소스를 생성하려 하면 policy상 거부되어 403이 반환된다. 코드 레벨의 gap(nil 참조, 잘못된 SQL/schema, 로직 오류)은 존재하지 않는다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-api "Archived entity"
(참고: service:cupixworks-api "PermissionDenied" 및 "Cupix::Errors::PermissionDenied" 쿼리는 0건 — 로그에는 클래스 전체 경로 대신 error.class 필드에만 기록됨.)
최근 발생 로그는 모두 status: info이며 create 액션에 대한 403이다:
2026-08-02 00:23:58 [403] POST /api/v1/videos (Api::V1::VideosController#create) error.code=PERM32000
2026-08-01 23:56:43 [403] POST /api/v1/videos (Api::V1::VideosController#create) error.code=PERM32000
2026-08-01 09:53:53 [403] POST /api/v1/buildings (Api::V1::BuildingsController#create) error.code=PERM32000
2026-08-01 09:53:41 [403] POST /api/v1/facilities/po00en/mounts (Api::V1::MountsController#create) error.code=PERM32000
2026-07-31 11:43:56 [403] POST /api/v1/reviews (Api::V1::ReviewsController#create) error.code=PERM32000
2026-07-30 00:44:44 [403] POST /api/v1/videos (Api::V1::VideosController#create) error.code=PERM32000
- Representative Error("Archived entity")와 last_seen 부근 로그의
message/code/class가 완전히 일치 → Representative가 stale하지 않음. Error Tracking이 단일 메시지로 묶고 있으나 실제 최근 발생도 동일한 케이스다. - 모든 샘플이 info 레벨(403)로 exception/error 트레이스가 아님.
Status Board#
incident-board 조회 결과 scope는 svc:cupixworks-api::unknown이며 현재 active 인시던트 없음(2026-07-29, 07-30의 resolved 인시던트는 별개의 서비스 저하 이벤트로, 본 클러스터와 무관). 외부 dependency 아웃티지가 아님.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 의도된 archived-entity 권한 가드가 발생시키는 정상적 403 (noise) | base_factory.rb:151 명시적 raise; permission.yml:10 "Only administrator can modify archived entities"; 모든 로그 status: info / [403]; 18개월 21건 저빈도 |
— | Confirmed |
| H2 | Representative Error가 stale하고 실제로는 다른 최신 에러가 발생 중 | — | last_seen(2026-08-02) 로그의 message/code/class가 Representative와 동일 | Rejected |
| H3 | nil 참조 등 코드 버그로 인한 예외 | — | check_archived_entity는 respond_to?·present? 가드 후 명시적 raise, 트레이스가 아닌 제어된 403; error/warn 레벨 로그 0건 |
Rejected |
| H4 | 외부 dependency 아웃티지 | — | status board scope svc:*, active 없음 |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
코드 수정 불필요. 이 403은 archived parent에 하위 리소스를 만들려는 비관리자 요청에 대한 정상적인 정책 거부다. error.reason/error.code가 이미 명확한 메시지를 제공하고 있어 서버 측 변경 대상이 아니다.
단기 개선 (1주 이내)#
- (선택) 프런트엔드가 이미 archived 상태의 parent에 대해 생성 UI를 비활성화하도록 클라이언트 UX를 점검하면 불필요한 403 요청을 줄일 수 있다. 이는 API 버그가 아니라 클라이언트 조율 사항이므로 자동 code-fix 대상에서 제외한다.
장기 개선 (재발 방지)#
- Error Tracking에서 PERM32000 (
Cupix::Errors::PermissionDenied/ 403 info)을 error 이슈로 수집하지 않도록 필터링/무시 처리하여 노이즈로 재수집되지 않게 한다.
Monitoring#
정상 비즈니스 응답이므로 알림이 필요하지 않으나, 비정상적 급증(오작동한 클라이언트, 잘못된 아카이브 상태 전파 등)을 가시화하려면 다음 timeseries 쿼리를 사용할 수 있다.
service:cupixworks-api "Archived entity"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (코드 변경 없음, noise 처리 대상)
Noise Verdict#
noise — archived 상태의 parent에 비관리자가 하위 리소스를 생성하려다 거부되는 의도된 권한 검증 실패(HTTP 403, info 레벨)로 코드 수정이 필요 없다.