ES /docs

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.rbcheck_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#

  1. 2025-02-07 14:55 KST — 최초 발생 (first_seen, Error Tracking 등록)
  2. 2026-07-30 09:10~09:44 KSTPOST /api/v1/videos에서 다수 403 발생
  3. 2026-08-01 18:47~18:53 KSTPOST /api/v1/buildings, /mounts에서 403 다발
  4. 2026-08-02 00:23 KST — last_seen (POST /api/v1/videos)
  5. 2026-08-04 KST — RCA 수행

Error Log#

Datadog Logs

text
Archived entity

최근 발생 로그(last_seen 부근)의 원문:

json
{
  "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_statearchived 또는 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 → factory create! 호출
  • Guard: app/factories/base_factory.rb:83check_archived_entity 호출
  • Failure point: app/factories/base_factory.rb:151 — PERM32000 raise

create!는 하위 엔티티를 저장하기 전에 archived 가드를 먼저 실행한다.

app/factories/base_factory.rb:80-88ruby
  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을 발생시킨다.

app/factories/base_factory.rb:149-153ruby
  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

에러 코드 정의상 이 응답의 의미는 "아카이브된 엔티티는 관리자만 수정할 수 있음"이다.

config/error_code/permission.yml:10-11yaml
PERM32000:
  message: "Permission denied: Only administrator can modify archived entities"

기대 동작 vs 실제 동작: 기대 동작 그대로다 — 비관리자 사용자가 아카이브된 parent에 하위 리소스를 생성하려 하면 policy상 거부되어 403이 반환된다. 코드 레벨의 gap(nil 참조, 잘못된 SQL/schema, 로직 오류)은 존재하지 않는다.

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-api "Archived entity"

(참고: service:cupixworks-api "PermissionDenied""Cupix::Errors::PermissionDenied" 쿼리는 0건 — 로그에는 클래스 전체 경로 대신 error.class 필드에만 기록됨.)

최근 발생 로그는 모두 status: info이며 create 액션에 대한 403이다:

text
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_entityrespond_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 쿼리를 사용할 수 있다.

text
service:cupixworks-api "Archived entity"

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial (코드 변경 없음, noise 처리 대상)

Noise Verdict#

noise — archived 상태의 parent에 비관리자가 하위 리소스를 생성하려다 거부되는 의도된 권한 검증 실패(HTTP 403, info 레벨)로 코드 수정이 필요 없다.