Files
tech-log-backend/docs/registries/error-codes.yaml
T
DongHyeonkaandClaude Opus 5 561d02ae3a feat: 프로젝트 게시와 홈 focus, 그리고 기록 사이 연결을 실제로 가능하게 한다
계약에 선언만 되어 있고 구현이 없던 네 오퍼레이션과, 의도된 스텁으로 남아 있던
catalog 두 종류가 공개 화면 다섯 곳을 조용히 비워 두고 있었다.

프로젝트 게시 — 프로젝트는 `RecordKind` 에 없어 문서 게시 파이프라인을 타지 못하는데,
공개 조회들(프로젝트 목록·상세·프로필의 "현재 프로젝트"·홈 focus)은 전부
`public_resource_projection` 의 PROJECT 행을 가시성 관문으로 쓴다. 그 행을 세우는
경로가 없었으므로 프로젝트는 만들 수는 있어도 공개될 수는 없었다. 릴리스가 자체 경로를
갖는 것과 같은 이유로, 문서 파이프라인에 끼워 넣지 않고 원본 상태·투영·canonical
route 세 곳을 한 트랜잭션에서 함께 세운다.

홈 focus — `home_focus_config` 는 마이그레이션이 빈 행 하나만 넣어 두었고 그 값을
읽고 쓸 use case 가 없었다. 지목한 대상이 실제로 있는지는 여기서 확인한다. 테이블에
FK 가 없어(설정이 대상보다 오래 살아남는 것을 허용하는 설계다) 없는 id 도 저장되고,
그러면 공개 화면은 조용히 빈 focus 를 그린다 — 저장은 성공했는데 아무것도 안 나오는,
이유를 알 수 없는 실패가 된다. 대상이 공개인지는 확인하지 않는다: 미리 지목해 두고
게시와 동시에 뜨게 하는 것이 정상적인 순서다.

catalog RELATION/EVIDENCE — 「슬라이스 2·5에서 채운다」는 주석과 함께 `List.of()` 로
남아 있었다. 그래서 어떤 기록도 연결 대상 목록을 채울 수 없었다. RELATION 은 작성
중에 고르는 것이라 작업본까지 포함하고(두 문서를 같이 쓰면서 서로 잇는 것이 정상적인
순서다), EVIDENCE 는 읽는 사람이 따라갈 수 있어야 하므로 공개된 것만 포함한다.

계약은 이 네 오퍼레이션을 ProblemDetails 모양으로 두고 있었다. 백엔드가 모든 JSON
응답을 envelope 으로 감싸므로 구현하는 순간 어긋난다 — 나머지와 같은 모양으로 옮겼다.
`/home-focus` 는 케밥 세그먼트라 D19(AIP-122)를 위반해 `/home/focus` 로 나눴다.

함께 고친 것들 (모두 이 작업 전부터 빨간 상태였다):
- `error-codes.yaml` 의 DOCUMENT_NOT_FOUND 가 표면마다 하나씩 두 행이었다. 이
  레지스트리의 식별자는 code 하나뿐이라 로딩 자체가 깨졌고, 그 여파로 거버넌스 테스트
  네 개와 outbox 계약 테스트가 함께 넘어졌다. 같은 코드는 같은 말을 해야 한다.
- 벤더된 계약 세 개의 MANIFEST.sha256 이 실제 파일과 어긋나 있었다.
- ActuatorSecurityHttpTest 는 "DB·Redis 없는 슬라이스"라고 적어 두고 Redis 자동설정을
  막지 않아, localhost:6379 연결 실패가 /actuator/health 를 503 으로 만들었다. 보안
  태세와 무관한 이유로 빨개지던 테스트다.
- 테스트 JVM 힙이 Gradle 기본 512m 이라 Spring context 캐시 + ArchUnit +
  Testcontainers 조합에서 OOM 이 났다. 증상이 테스트 실패가 아니라 "Executor 를 완료할
  수 없음"이어서 원인을 가리켰다.
- APP_SESSION_TIMEOUT 이 env-keys 레지스트리에 없었다.

새 SQL 은 실제 PostgreSQL 위에서 돌린다 — 컴파일도 단위 테스트도 컬럼 이름을 검증하지
못한다는 것이 이 파일이 존재하는 이유다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEHXspz4rv5pB5wiiSsVDu
2026-08-23 18:19:07 +09:00

1488 lines
62 KiB
YAML

# Registry: Error Codes
# SSOT: wiki/projects/ca-tmpl/registries/error-codes.yaml
# Schema owner: feature-contract-registry-governance
# Category enum owner: feature-operational-error-observability-foundation
# Last updated: 2026-05-22
# Note: 이 파일은 Phase B 산출물. Phase C2(ca-tmpl 실 코드)에서 generated Java constants의 source.
#
# Schema (per row):
# code: UPPER_SNAKE_CASE
# category: VALIDATION | AUTH | AUTHZ | NOT_FOUND | CONFLICT |
# RATE_LIMIT | TRANSIENT_DEPENDENCY | PERMANENT_DEPENDENCY |
# DATA_INTEGRITY | INTERNAL
# http_status: int (async-only failures use 500 placeholder)
# retryable: bool
# retry_after_seconds: int | null (RATE_LIMIT/TRANSIENT 권고 backoff)
# owner_branch: source branch (raw/branch-notes/feature-*.md)
# owner_layer: presentation | application | domain | infrastructure | crosscut
# client_safe_message: no token / no principal raw / no internal path / no stack trace
# log_level: ERROR | WARN | INFO
# runbook_link: runbook://area/scenario OR null (client-error만 null 허용)
# compatibility_impact: none | additive | behavior-change | breaking
# required_test: owning contract test identifier
#
# Runbook policy (operational-runbook-contract L80):
# retryable=false + category ∈ {AUTH, AUTHZ, RATE_LIMIT, INTERNAL,
# TRANSIENT_DEPENDENCY, PERMANENT_DEPENDENCY} ⇒ runbook_link 필수.
# VALIDATION/NOT_FOUND/CONFLICT/DATA_INTEGRITY는 client-error로 runbook 면제 가능.
# retryable=true 인 모든 row는 runbook_link 필수.
errors:
# ============================================================
# AUTH (feature-security-operational-baseline / Decision Matrix)
# ============================================================
# source: feature-security-operational-baseline L82 — "token 누락 | 401 | AUTH_TOKEN_MISSING | AUTH"
- code: AUTH_TOKEN_MISSING
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication required"
log_level: WARN
runbook_link: "runbook://auth/token-missing"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L83 — "token malformed (parse fail) | 401 | AUTH_TOKEN_MALFORMED | AUTH"
- code: AUTH_TOKEN_MALFORMED
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication failed"
log_level: WARN
runbook_link: "runbook://auth/token-malformed"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L84 — "token expired (clock skew tolerance 60s 초과) | 401 | AUTH_TOKEN_EXPIRED | AUTH"
- code: AUTH_TOKEN_EXPIRED
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication expired"
log_level: WARN
runbook_link: "runbook://auth/token-expired"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L85 — "invalid signature | 401 | AUTH_TOKEN_INVALID_SIGNATURE | AUTH"
- code: AUTH_TOKEN_INVALID_SIGNATURE
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication failed"
log_level: ERROR
runbook_link: "runbook://auth/token-invalid-signature"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L86 — "issuer mismatch | 401 | AUTH_ISSUER_MISMATCH | AUTH"
- code: AUTH_ISSUER_MISMATCH
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication failed"
log_level: ERROR
runbook_link: "runbook://auth/issuer-mismatch"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L87 — "audience mismatch | 401 | AUTH_AUDIENCE_MISMATCH | AUTH"
- code: AUTH_AUDIENCE_MISMATCH
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication failed"
log_level: ERROR
runbook_link: "runbook://auth/audience-mismatch"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L88 — "unknown kid (JWKS 미캐시) | 401 + Retry-After 5s | AUTH_KID_UNKNOWN | AUTH"
- code: AUTH_KID_UNKNOWN
category: AUTH
http_status: 401
retryable: true # 2026-06-01: false→true. JWKS 키 회전 중 unknown kid 는 ~5s 후 JWKS refresh 로 해소 가능(transient). retry_after_seconds=5 + client_safe_message "please retry" 와 정합. 키 고정 정책으로 전환 시 false 복귀.
retry_after_seconds: 5
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication failed, please retry"
log_level: WARN
runbook_link: "runbook://auth/kid-unknown"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L89 — "JWKS endpoint outage ... | AUTH_JWKS_UNAVAILABLE | TRANSIENT_DEPENDENCY"
- code: AUTH_JWKS_UNAVAILABLE
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 30
owner_branch: feature-security-operational-baseline
owner_layer: infrastructure
client_safe_message: "Authentication service temporarily unavailable"
log_level: ERROR
runbook_link: "runbook://auth/jwks-unavailable"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-security-operational-baseline L90 — "claim mapping failure ... | 401 | AUTH_CLAIM_MAPPING_FAILED | AUTH"
- code: AUTH_CLAIM_MAPPING_FAILED
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Authentication failed"
log_level: ERROR
runbook_link: "runbook://auth/claim-mapping-failed"
compatibility_impact: none
required_test: contract-verification:auth-category
# ============================================================
# AUTHZ (feature-security-operational-baseline)
# ============================================================
# source: feature-security-operational-baseline L91 — "valid token + 권한 부족 | 403 | AUTHZ_INSUFFICIENT_PERMISSION | AUTHZ"
- code: AUTHZ_INSUFFICIENT_PERMISSION
category: AUTHZ
http_status: 403
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Permission denied"
log_level: WARN
runbook_link: "runbook://authz/insufficient-permission"
compatibility_impact: none
required_test: contract-verification:authz-category
# source: feature-security-operational-baseline L92 — "valid token + tenant cross-access | 403 | AUTHZ_TENANT_MISMATCH | AUTHZ"
- code: AUTHZ_TENANT_MISMATCH
category: AUTHZ
http_status: 403
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: presentation
client_safe_message: "Permission denied"
log_level: ERROR
runbook_link: "runbook://authz/tenant-mismatch"
compatibility_impact: none
required_test: contract-verification:authz-category
# ============================================================
# INTERNAL (feature-security-operational-baseline + container-runtime)
# ============================================================
# source: feature-security-operational-baseline L93 — "public path misconfiguration ... | 500 + P1 alert | INTERNAL_AUTH_MISCONFIGURATION | INTERNAL"
- code: INTERNAL_AUTH_MISCONFIGURATION
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-security-operational-baseline
owner_layer: crosscut
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://auth/public-path-misconfiguration"
compatibility_impact: none
required_test: contract-verification:auth-category
# source: feature-container-runtime-contract L113 — "JVM OutOfMemoryError → ExitOnOutOfMemoryError로 137 exit, log에 error.code=JVM_OOM 명시"
- code: JVM_OOM
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-container-runtime-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://runtime/jvm-oom"
compatibility_impact: none
required_test: contract-verification:container-runtime-oom
# ============================================================
# DB / Persistence (feature-persistence-failure-baseline / SQLState Matrix)
# ============================================================
# source: feature-persistence-failure-baseline L85 — "08* | all | TRANSIENT_DEPENDENCY | DB_UNAVAILABLE | true"
- code: DB_UNAVAILABLE
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 5
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Service temporarily unavailable"
log_level: ERROR
runbook_link: "runbook://db/unavailable"
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L86 — "40001 | Postgres/MySQL | CONFLICT | DB_SERIALIZATION_FAILURE | true"
- code: DB_SERIALIZATION_FAILURE
category: CONFLICT
http_status: 409
retryable: true
retry_after_seconds: 1
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Request conflicted with another transaction, please retry"
log_level: WARN
runbook_link: "runbook://db/serialization-failure"
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L87 — "40P01 | Postgres | CONFLICT | DB_DEADLOCK | true (backoff)"
- code: DB_DEADLOCK
category: CONFLICT
http_status: 409
retryable: true
retry_after_seconds: 1
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Request conflicted, please retry"
log_level: WARN
runbook_link: "runbook://db/deadlock"
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L88 — "23502 | Postgres | DATA_INTEGRITY | DB_NULL_VIOLATION | false"
- code: DB_NULL_VIOLATION
category: DATA_INTEGRITY
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Request violates a required field constraint"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L89 — "23503 | Postgres | DATA_INTEGRITY | DB_FK_VIOLATION | false"
- code: DB_FK_VIOLATION
category: DATA_INTEGRITY
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Request references missing resource"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L90 — "23505 | Postgres | CONFLICT | DB_UNIQUE_VIOLATION | false (business mapping)"
- code: DB_UNIQUE_VIOLATION
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Resource already exists"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L91 — "23514 | Postgres | DATA_INTEGRITY | DB_CHECK_VIOLATION | false"
- code: DB_CHECK_VIOLATION
category: DATA_INTEGRITY
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Request violates a value constraint"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L92 — "25P03 | Postgres | TRANSIENT_DEPENDENCY | DB_IDLE_IN_TX_TIMEOUT | true"
- code: DB_IDLE_IN_TX_TIMEOUT
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 2
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Service temporarily unavailable"
log_level: ERROR
runbook_link: "runbook://db/idle-in-tx-timeout"
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# source: feature-persistence-failure-baseline L93 — "57014 | Postgres | TRANSIENT_DEPENDENCY | DB_QUERY_CANCELED | false"
- code: DB_QUERY_CANCELED
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: false
retry_after_seconds: null
owner_branch: feature-persistence-failure-baseline
owner_layer: infrastructure
client_safe_message: "Request was canceled, please retry later"
log_level: WARN
runbook_link: "runbook://db/query-canceled"
compatibility_impact: none
required_test: contract-verification:persistence-mapping
# ============================================================
# Rate limit / Idempotency (feature-rate-limit-idempotency-contract)
# ============================================================
# source: feature-rate-limit-idempotency-contract — rate limit response/log 기준 / Retry-After header 기준 (scope L29, L33)
- code: RATE_LIMIT_EXCEEDED
category: RATE_LIMIT
http_status: 429
retryable: true
retry_after_seconds: 1
owner_branch: feature-rate-limit-idempotency-contract
owner_layer: presentation
client_safe_message: "Too many requests, please retry after the indicated interval"
log_level: WARN
runbook_link: "runbook://rate-limit/exceeded"
compatibility_impact: none
required_test: contract-verification:rate-limit
# source: feature-rate-limit-idempotency-contract L71 — "200ms 초과 시 409 IDEMPOTENT_IN_FLIGHT (retryable=false, client는 polling)"
- code: IDEMPOTENT_IN_FLIGHT
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-rate-limit-idempotency-contract
owner_layer: application
client_safe_message: "A previous identical request is still being processed, please poll for result"
log_level: INFO
runbook_link: null
compatibility_impact: none
required_test: contract-verification:idempotency
# source: feature-rate-limit-idempotency-contract L72 — "fingerprint mismatch (same key + different body) = 422 IDEMPOTENT_REQUEST_MISMATCH"
- code: IDEMPOTENT_REQUEST_MISMATCH
category: VALIDATION
http_status: 422
retryable: false
retry_after_seconds: null
owner_branch: feature-rate-limit-idempotency-contract
owner_layer: application
client_safe_message: "Idempotency key reused with different request body"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:idempotency
# ============================================================
# File / Resource (feature-file-resource-handling-contract)
# ============================================================
# source: feature-file-resource-handling-contract L69 — "spring.servlet.multipart.max-file-size 10MB ... Spring 단의 enforcement가 실패 시 envelope 응답 보장" / 테스트 계약 "oversized upload가 generic 500으로 처리되면 실패"
- code: UPLOAD_SIZE_EXCEEDED
category: VALIDATION
http_status: 413
retryable: false
retry_after_seconds: null
owner_branch: feature-file-resource-handling-contract
owner_layer: presentation
client_safe_message: "Uploaded file exceeds maximum size"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:file-upload
# source: feature-file-resource-handling-contract L72 — "allowed content-type allowlist starting set ..."
- code: UPLOAD_CONTENT_TYPE_REJECTED
category: VALIDATION
http_status: 415
retryable: false
retry_after_seconds: null
owner_branch: feature-file-resource-handling-contract
owner_layer: presentation
client_safe_message: "Uploaded content type is not allowed"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:file-upload
# source: feature-file-resource-handling-contract — Decisionized Work Items "path traversal | normalized storage key only ... | traversal test"
- code: PATH_TRAVERSAL_DETECTED
category: VALIDATION
http_status: 400
retryable: false
retry_after_seconds: null
owner_branch: feature-file-resource-handling-contract
owner_layer: presentation
client_safe_message: "Invalid file path"
log_level: ERROR
runbook_link: null
compatibility_impact: none
required_test: contract-verification:file-upload
# source: feature-file-resource-handling-contract L73 — "streaming download backpressure = response timeout 60s, max stream 100MB. 초과 시 truncate + ERROR log"
- code: DOWNLOAD_STREAMING_FAILURE
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 5
owner_branch: feature-file-resource-handling-contract
owner_layer: presentation
client_safe_message: "Download failed, please retry"
log_level: ERROR
runbook_link: "runbook://file/download-streaming-failure"
compatibility_impact: none
required_test: contract-verification:file-download
# ============================================================
# API contract transport-standard codes (feature-api-contract-baseline)
# ============================================================
# NOTE: feature-api-contract-baseline owns the transport-shape failure
# classification (D8 413/414, D9 406/415, D12 405, D15 412). These rows mirror
# dev.caskeleton.shared.error.OperationalError; the D11 status-mapping
# consistency test (owner: this branch, producer) fails the build when a code's
# registry http_status and the enum httpStatus() drift apart.
# source: feature-api-contract-baseline.md D12 — "405 Method Not Allowed + Allow header 의무"
- code: METHOD_NOT_ALLOWED
category: VALIDATION
http_status: 405
retryable: false
retry_after_seconds: null
owner_branch: feature-api-contract-baseline
owner_layer: presentation
client_safe_message: "HTTP method not allowed for this resource"
log_level: INFO
runbook_link: null
compatibility_impact: none
required_test: contract-verification:api-contract-status-mapping
# source: feature-api-contract-baseline.md D9 — "406 Not Acceptable = 응답 표현 협상 실패"
- code: NOT_ACCEPTABLE
category: VALIDATION
http_status: 406
retryable: false
retry_after_seconds: null
owner_branch: feature-api-contract-baseline
owner_layer: presentation
client_safe_message: "No acceptable representation for the requested Accept header"
log_level: INFO
runbook_link: null
compatibility_impact: none
required_test: contract-verification:api-contract-status-mapping
# source: feature-api-contract-baseline.md D15 — "If-Match mismatch 시 412 Precondition Failed"
- code: PRECONDITION_FAILED
category: CONFLICT
http_status: 412
retryable: false
retry_after_seconds: null
owner_branch: feature-api-contract-baseline
owner_layer: presentation
client_safe_message: "Resource was modified by another request; refetch and retry"
log_level: INFO
runbook_link: null
compatibility_impact: none
required_test: contract-verification:api-contract-status-mapping
# source: feature-api-contract-baseline.md D8 — "request size limit 실패 분류 (413)"
- code: PAYLOAD_TOO_LARGE
category: VALIDATION
http_status: 413
retryable: false
retry_after_seconds: null
owner_branch: feature-api-contract-baseline
owner_layer: presentation
client_safe_message: "Request payload is too large"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:api-contract-status-mapping
# source: feature-api-contract-baseline.md D8 형제 — "URI 길이 실패 분류 (414)"
# NOTE: enforcement is Tomcat/gateway-owned (rejected before Spring dispatch);
# this row + code exist for status-mapping consistency. End-to-end 414 contract
# test is `planned` (gateway/Tomcat maxHttpHeaderSize 8KB boundary).
- code: URI_TOO_LONG
category: VALIDATION
http_status: 414
retryable: false
retry_after_seconds: null
owner_branch: feature-api-contract-baseline
owner_layer: presentation
client_safe_message: "Request URI is too long"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:api-contract-status-mapping
# source: feature-api-contract-baseline.md D9 — "415 Unsupported Media Type = 요청 본문 format 미지원"
- code: UNSUPPORTED_MEDIA_TYPE
category: VALIDATION
http_status: 415
retryable: false
retry_after_seconds: null
owner_branch: feature-api-contract-baseline
owner_layer: presentation
client_safe_message: "Request Content-Type is not supported"
log_level: INFO
runbook_link: null
compatibility_impact: none
required_test: contract-verification:api-contract-status-mapping
# ============================================================
# Tenant (feature-tenant-context-policy)
# ============================================================
# source: feature-tenant-context-policy L71 — "tenant 미지원 모드에서 X-Tenant-Id 헤더 수신 시 400 TENANT_NOT_SUPPORTED (filter 단계)"
- code: TENANT_NOT_SUPPORTED
category: VALIDATION
http_status: 400
retryable: false
retry_after_seconds: null
owner_branch: feature-tenant-context-policy
owner_layer: presentation
client_safe_message: "Tenant context is not supported by this deployment"
log_level: WARN
runbook_link: null
compatibility_impact: none
required_test: contract-verification:tenant-policy
# ============================================================
# Validation / Business rule (feature-business-rule-validation-contract)
# ============================================================
# NOTE: business-rule-validation branch는 mapping 규칙 SSOT (syntax→VALIDATION,
# policy→AUTHZ/CONFLICT, invariant→CONFLICT/VALIDATION, persistence→PERSISTENCE/CONFLICT)
# 이며 구체 code는 example로 VALIDATION_EMAIL_FORMAT만 등장
# (feature-operational-error-observability-foundation L110). 실제 도메인별 code는
# Phase D(도메인 feature 적용) 시 본 registry에 추가.
# source: feature-operational-error-observability-foundation L110 — "code: VALIDATION_EMAIL_FORMAT, // registry-registered code" (validation field error JSON shape example)
- code: VALIDATION_EMAIL_FORMAT
category: VALIDATION
http_status: 400
retryable: false
retry_after_seconds: null
owner_branch: feature-operational-error-observability-foundation
owner_layer: presentation
client_safe_message: "Invalid email format"
log_level: INFO
runbook_link: null
compatibility_impact: none
required_test: contract-verification:validation-envelope
# ============================================================
# Cache (feature-cache-consistency-contract)
# ============================================================
# source: feature-cache-consistency-contract — Decisionized Work Items "Redis unavailable | degrade only if declared | fail-fast for required cache | generic INTERNAL | unavailable mapping" / 테스트 "Redis unavailable이 degrade 가능 여부 없이 INTERNAL로 처리되면 실패"
- code: CACHE_UNAVAILABLE
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 2
owner_branch: feature-cache-consistency-contract
owner_layer: infrastructure
client_safe_message: "Service temporarily unavailable"
log_level: ERROR
runbook_link: "runbook://cache/unavailable"
compatibility_impact: none
required_test: contract-verification:cache-consistency
# source: feature-cache-consistency-contract L70 — "stampede 방지 default = single-instance Caffeine local lock, multi-instance HPA 시 Redisson RLock distributed mutex" / 테스트 "동일 key에 대해 동시 cache miss 시 backend 호출이 1회로 제한되는지 verify (stampede). 미충족 시 실패"
- code: CACHE_STAMPEDE_LOCK_TIMEOUT
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 1
owner_branch: feature-cache-consistency-contract
owner_layer: infrastructure
client_safe_message: "Service temporarily unavailable"
log_level: WARN
runbook_link: "runbook://cache/stampede-lock-timeout"
compatibility_impact: none
required_test: contract-verification:cache-consistency
# ============================================================
# Outbound HTTP (feature-outbound-http-client-baseline)
# ============================================================
# source: feature-outbound-http-client-baseline L70 — "outbound HTTP timeout default = connect 2s / read 5s / global call 10s" + scope "timeout/connect/DNS failure 분류" / 테스트 "upstream timeout은 retryable dependency failure로 분류되어야 함"
- code: DEPENDENCY_TIMEOUT
category: TRANSIENT_DEPENDENCY
http_status: 504
retryable: true
retry_after_seconds: 2
owner_branch: feature-outbound-http-client-baseline
owner_layer: infrastructure
client_safe_message: "Upstream service did not respond in time, please retry"
log_level: ERROR
runbook_link: "runbook://dependency/timeout"
compatibility_impact: none
required_test: contract-verification:outbound-http
# source: feature-outbound-http-client-baseline — scope "timeout/connect/DNS failure 분류" + L70 connect=2s timeout
- code: DEPENDENCY_CONNECT_FAILED
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 2
owner_branch: feature-outbound-http-client-baseline
owner_layer: infrastructure
client_safe_message: "Upstream service unreachable, please retry"
log_level: ERROR
runbook_link: "runbook://dependency/connect-failed"
compatibility_impact: none
required_test: contract-verification:outbound-http
# source: feature-outbound-http-client-baseline — scope "timeout/connect/DNS failure 분류"
- code: DEPENDENCY_DNS_FAILED
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 5
owner_branch: feature-outbound-http-client-baseline
owner_layer: infrastructure
client_safe_message: "Upstream service unreachable, please retry"
log_level: ERROR
runbook_link: "runbook://dependency/dns-failed"
compatibility_impact: none
required_test: contract-verification:outbound-http
# source: feature-outbound-http-client-baseline — scope "upstream 4xx/5xx 분류" / 테스트 "401/403은 credential/scope/config 문제로 분류되어야 함"
- code: DEPENDENCY_4XX_CLIENT
category: PERMANENT_DEPENDENCY
http_status: 502
retryable: false
retry_after_seconds: null
owner_branch: feature-outbound-http-client-baseline
owner_layer: infrastructure
client_safe_message: "Upstream service rejected the request"
log_level: ERROR
runbook_link: "runbook://dependency/4xx-client"
compatibility_impact: none
required_test: contract-verification:outbound-http
# source: feature-outbound-http-client-baseline — scope "upstream 4xx/5xx 분류"
- code: DEPENDENCY_5XX_SERVER
category: TRANSIENT_DEPENDENCY
http_status: 502
retryable: true
retry_after_seconds: 2
owner_branch: feature-outbound-http-client-baseline
owner_layer: infrastructure
client_safe_message: "Upstream service error, please retry"
log_level: ERROR
runbook_link: "runbook://dependency/5xx-server"
compatibility_impact: none
required_test: contract-verification:outbound-http
# source: feature-outbound-http-client-baseline L69 — "circuit breaker metric은 dependency.name, dependency.type, outcome까지만 tag로 허용" + Decisionized "circuit breaker | Resilience4j optional env"
- code: DEPENDENCY_CIRCUIT_OPEN
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 10
owner_branch: feature-outbound-http-client-baseline
owner_layer: infrastructure
client_safe_message: "Upstream service temporarily unavailable, please retry later"
log_level: WARN
runbook_link: "runbook://dependency/circuit-open"
compatibility_impact: none
required_test: contract-verification:outbound-http
# ============================================================
# Outbox (feature-domain-event-outbox-contract)
# ============================================================
# source: feature-domain-event-outbox-contract L67 — "outbox row status enum = PENDING / IN_FLIGHT / PUBLISHED / FAILED / DEAD" + scope "publish 실패 분류" / 판정 "publish 실패가 retry/DLQ/log/runbook 기준 없이 삼켜지면 실패"
- code: OUTBOX_PUBLISH_FAILED
category: TRANSIENT_DEPENDENCY
http_status: 500
retryable: true
retry_after_seconds: 30
owner_branch: feature-domain-event-outbox-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://outbox/publish-failed"
compatibility_impact: none
required_test: contract-verification:outbox-publish
# source: feature-domain-event-outbox-contract L67 — outbox status enum "DEAD" / Outbox Defaults "DLQ | background-job branch owner"
- code: OUTBOX_DEAD_LETTER
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-domain-event-outbox-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://outbox/dead-letter"
compatibility_impact: none
required_test: contract-verification:outbox-dlq
# ============================================================
# Background job / Async (feature-background-job-async-contract)
# ============================================================
# source: feature-background-job-async-contract — Decisionized "saturation | bounded executor + rejection log" / L72 "saturation policy default = AbortPolicy" / 테스트 "executor rejection이 structured log 없이 발생하면 실패"
- code: JOB_EXECUTOR_REJECTED
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 5
owner_branch: feature-background-job-async-contract
owner_layer: infrastructure
client_safe_message: "Service temporarily unavailable"
log_level: ERROR
runbook_link: "runbook://job/executor-rejected"
compatibility_impact: none
required_test: contract-verification:async-saturation
# source: feature-background-job-async-contract L69 — "기본 backoff는 exponential backoff with jitter, max attempts 3, DLQ after exhausted attempts" + scope "shutdown 중 job 처리 기준" / L73 graceful shutdown ≤19s
- code: JOB_TIMEOUT
category: TRANSIENT_DEPENDENCY
http_status: 500
retryable: true
retry_after_seconds: 10
owner_branch: feature-background-job-async-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://job/timeout"
compatibility_impact: none
required_test: contract-verification:async-timeout
# source: feature-background-job-async-contract L69 — "DLQ after exhausted attempts" + Decisionized "retry/DLQ | exp backoff jitter, max 3, DLQ exhausted | ... | infinite retry"
- code: JOB_DEAD_LETTER
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-background-job-async-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://job/dead-letter"
compatibility_impact: none
required_test: contract-verification:async-dlq
# ============================================================
# Distributed Lock (feature-distributed-lock-contract)
# ============================================================
# source: feature-distributed-lock-contract D7 — "lock 획득 실패/timeout 의 error code =
# LOCK_ACQUISITION_TIMEOUT (category CONFLICT, retryable true, client_safe true) + metric
# lock.acquisition" / D5 — "try-lock + 유한 waitTime + lease(TTL) 필수, 무한 blocking 금지".
# category CONFLICT 는 기존 enum 재사용; retryable=true — 락 보유자가 임계 구역을 빠져나오면
# 동일 요청 재시도로 해소된다(transient contention). DB_DEADLOCK / DB_SERIALIZATION_FAILURE 와
# 같은 retryable CONFLICT 계열(409). 본 코드는 distributedLockProvider 획득 timeout 전용이며
# cache stampede lock 의 CACHE_STAMPEDE_LOCK_TIMEOUT(cache-consistency, TRANSIENT_DEPENDENCY 503)
# 과 의미가 구분된다 — 후자는 캐시 백엔드 의존성 timeout, 전자는 분산 상호배제 contention.
- code: LOCK_ACQUISITION_TIMEOUT
category: CONFLICT
http_status: 409
retryable: true
retry_after_seconds: 1
owner_branch: feature-distributed-lock-contract
owner_layer: infrastructure
client_safe_message: "Resource is busy, please retry"
log_level: WARN
runbook_link: "runbook://lock/acquisition-timeout"
compatibility_impact: none
required_test: contract-verification:lock-acquisition-timeout
# ============================================================
# Migration / Startup (feature-migration-startup-contract)
# ============================================================
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = ... migration 실패=70 ..." + Decisionized "startup failure log | structured log with startup.phase, error.code, error.category"
- code: MIGRATION_FAILED
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-migration-startup-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://migration/failed"
compatibility_impact: none
required_test: contract-verification:migration-startup
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = env 누락/malformed=78 ..." / 테스트 "required env 누락 시 startup이 성공하면 실패"
- code: STARTUP_VALIDATION_FAILED
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-migration-startup-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://startup/validation-failed"
compatibility_impact: none
required_test: contract-verification:migration-startup
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = ... required adapter disabled=72" / 테스트 "disabled required adapter로 app이 뜨면 실패"
- code: REQUIRED_ADAPTER_DISABLED
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-migration-startup-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://startup/required-adapter-disabled"
compatibility_impact: none
required_test: contract-verification:migration-startup
# source: feature-integration-adapter-templates §구현 가이드 §4 (Layer 3) + §Audit A2.
# Runtime-lifecycle fail-fast for an invoke against a DISABLED optional adapter
# (Kafka/Redis/Slack/Google Email). Deliberately distinct from the startup-lifecycle
# REQUIRED_ADAPTER_DISABLED above (exit 72): a runtime invoke ≠ a startup validation,
# so reusing the startup code would conflate two lifecycles (A2 resolution — new
# runtime code owned by this branch). retryable=false: the adapter stays disabled
# until redeploy, so retrying the same call never clears it.
- code: ADAPTER_DISABLED
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-integration-adapter-templates
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://adapter/adapter-disabled"
compatibility_impact: none
required_test: adapter-contract:adapter-disabled-runtime-call
# source: feature-migration-startup-contract L71 — "startup exit code 표준 = ... profile mismatch=71" / 테스트 "prod profile에서 local-only 설정이 켜지면 실패"
- code: PROFILE_MISMATCH
category: INTERNAL
http_status: 500
retryable: false
retry_after_seconds: null
owner_branch: feature-migration-startup-contract
owner_layer: infrastructure
client_safe_message: "Internal server error"
log_level: ERROR
runbook_link: "runbook://startup/profile-mismatch"
compatibility_impact: none
required_test: contract-verification:migration-startup
# ============================================================
# Management / Actuator (feature-management-actuator-security-contract)
# ============================================================
# source: feature-management-actuator-security-contract — Exposure Policy "env/configprops | forbidden" "heapdump/threaddump | forbidden unless break-glass runbook" "shutdown | forbidden" / 테스트 "prod에서 env/configprops endpoint가 노출되면 실패"
- code: ACTUATOR_FORBIDDEN
category: AUTHZ
http_status: 403
retryable: false
retry_after_seconds: null
owner_branch: feature-management-actuator-security-contract
owner_layer: presentation
client_safe_message: "Permission denied"
log_level: WARN
runbook_link: "runbook://management/actuator-forbidden"
compatibility_impact: none
required_test: contract-verification:management-actuator
# ============================================================
# TECH LOG STUDIO (studio-v1.yaml ApiError.code — 23종, feature-techlog-studio-backend)
#
# StudioError(dev.caskeleton.application.techlog.error.StudioError)와 1:1 매핑.
# category/http_status/retryable은 그 enum의 선언과 정확히 같아야 한다
# (StudioErrorRegistryTest가 코드 존재만 보고, 값 일치는 이 파일의 리뷰 책임).
#
# PAYLOAD_TOO_LARGE / UNSUPPORTED_MEDIA_TYPE은 StudioError에도 있지만 별도 row를
# 추가하지 않는다 — feature-api-contract-baseline이 이미 동일 code로 아래(L514,
# L545)에 VALIDATION/413/415/false, VALIDATION/415/false row를 갖고 있고 값이
# StudioError 선언과 정확히 일치한다. error-codes.yaml의 identity column은 `code`
# 하나뿐이라 같은 code로 두 번째 row를 추가하면 ContractRegistrySchemaGovernanceTest
# 의 "duplicate identity" 게이트가 깨진다. 즉 이 두 코드는 기존 row가 이미 커버한다.
# ============================================================
# source: studio-v1.yaml ApiError.code — AUTHENTICATION_REQUIRED (StudioError.AUTHENTICATION_REQUIRED)
- code: AUTHENTICATION_REQUIRED
category: AUTH
http_status: 401
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: presentation
client_safe_message: "Studio 인증이 필요합니다"
log_level: INFO
runbook_link: "runbook://auth/token-missing"
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — STUDIO_ACCESS_DENIED (StudioError.STUDIO_ACCESS_DENIED)
- code: STUDIO_ACCESS_DENIED
category: AUTHZ
http_status: 403
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "이 Studio 리소스에 접근할 권한이 없습니다"
log_level: WARN
runbook_link: "runbook://authz/insufficient-permission"
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml + studio-management-v1.yaml ApiError.code — DOCUMENT_NOT_FOUND
# (StudioError.DOCUMENT_NOT_FOUND, ManagementError.DOCUMENT_NOT_FOUND)
#
# 두 표면이 같은 코드를 쓴다. 이 레지스트리의 식별자는 code 하나뿐이므로 행도 하나다 —
# 한때 표면마다 행을 두고 문구를 다르게 적었고("작업본을 찾을 수 없습니다"), 그 중복이
# 레지스트리 로딩 자체를 깨뜨렸다. 같은 코드는 같은 말을 해야 한다.
- code: DOCUMENT_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "요청한 문서를 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — VERSION_CONFLICT (StudioError.VERSION_CONFLICT)
- code: VERSION_CONFLICT
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "저장된 version이 더 최신입니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — REQUEST_VALIDATION_FAILED (StudioError.REQUEST_VALIDATION_FAILED)
- code: REQUEST_VALIDATION_FAILED
category: VALIDATION
http_status: 422
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: presentation
client_safe_message: "요청 형식이 올바르지 않습니다"
log_level: WARN
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — DOCUMENT_VALIDATION_FAILED
# (StudioError.DOCUMENT_VALIDATION_FAILED). 원래 계약 이름은 VALIDATION_FAILED였으나
# 스켈레톤 전역 OperationalError.VALIDATION_FAILED(400, VALIDATION)와 code 문자열이
# 충돌해(같은 문자열, 다른 http_status) 개명했다 — controller 판정, 2026-08-19.
- code: DOCUMENT_VALIDATION_FAILED
category: VALIDATION
http_status: 422
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "문서 검증에 실패했습니다"
log_level: WARN
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — VALIDATION_STALE (StudioError.VALIDATION_STALE)
- code: VALIDATION_STALE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "검증 결과가 최신 문서 기준이 아닙니다. 다시 검증해 주세요"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — PREVIEW_NOT_FOUND (StudioError.PREVIEW_NOT_FOUND)
- code: PREVIEW_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "요청한 미리보기를 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — PREVIEW_STALE (StudioError.PREVIEW_STALE)
- code: PREVIEW_STALE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "미리보기가 최신 문서 기준이 아닙니다. 다시 생성해 주세요"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — PREVIEW_EXPIRED (StudioError.PREVIEW_EXPIRED)
- code: PREVIEW_EXPIRED
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "미리보기가 만료되었습니다. 다시 생성해 주세요"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — PUBLICATION_NOT_FOUND (StudioError.PUBLICATION_NOT_FOUND)
- code: PUBLICATION_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "요청한 게시물을 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — PUBLICATION_CONFLICT (StudioError.PUBLICATION_CONFLICT)
- code: PUBLICATION_CONFLICT
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "게시 작업이 다른 변경과 충돌했습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — PUBLICATION_EVENT_NOT_FOUND (StudioError.PUBLICATION_EVENT_NOT_FOUND)
- code: PUBLICATION_EVENT_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "요청한 게시 이벤트를 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — PUBLICATION_SNAPSHOT_NOT_FOUND (StudioError.PUBLICATION_SNAPSHOT_NOT_FOUND)
- code: PUBLICATION_SNAPSHOT_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "요청한 게시 스냅샷을 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — WARNING_ACKNOWLEDGEMENT_REQUIRED (StudioError.WARNING_ACKNOWLEDGEMENT_REQUIRED)
- code: WARNING_ACKNOWLEDGEMENT_REQUIRED
category: VALIDATION
http_status: 422
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "경고 확인이 필요합니다. 확인 후 다시 시도해 주세요"
log_level: WARN
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — IDEMPOTENCY_KEY_REUSED (StudioError.IDEMPOTENCY_KEY_REUSED)
- code: IDEMPOTENCY_KEY_REUSED
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "Idempotency 키가 다른 요청에 재사용되었습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — ASSET_NOT_FOUND (StudioError.ASSET_NOT_FOUND)
- code: ASSET_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "요청한 자산을 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — ASSET_NOT_READY (StudioError.ASSET_NOT_READY)
- code: ASSET_NOT_READY
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "자산 처리가 아직 완료되지 않았습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — ASSET_IN_USE (StudioError.ASSET_IN_USE)
- code: ASSET_IN_USE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "자산이 사용 중이라 이 작업을 수행할 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — ASSET_QUARANTINED (StudioError.ASSET_QUARANTINED)
- code: ASSET_QUARANTINED
category: DATA_INTEGRITY
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-studio-backend
owner_layer: application
client_safe_message: "자산이 격리 처리되어 사용할 수 없습니다"
log_level: WARN
runbook_link: null
compatibility_impact: additive
required_test: StudioErrorTest
# source: studio-v1.yaml ApiError.code — STUDIO_UNAVAILABLE (StudioError.STUDIO_UNAVAILABLE)
- code: STUDIO_UNAVAILABLE
category: TRANSIENT_DEPENDENCY
http_status: 503
retryable: true
retry_after_seconds: 5
owner_branch: feature-techlog-studio-backend
owner_layer: infrastructure
client_safe_message: "Studio 서비스를 일시적으로 사용할 수 없습니다. 잠시 후 다시 시도해 주세요"
log_level: ERROR
runbook_link: "runbook://studio/unavailable"
compatibility_impact: additive
required_test: StudioErrorTest
# === Tech Log Public (feature-techlog-public-v1) ===
#
# public-v1.yaml 의 ApiError.code 는 세 값이다. 나머지 하나 INTERNAL_ERROR 는 스켈레톤
# 공통 코드로 이미 이 레지스트리에 있으므로 여기서 다시 선언하지 않는다.
#
# Studio 와 이름을 겹치지 않게 한 이유: 이 레지스트리는 코드 하나에 http_status 하나만
# 담는다. public 의 400 과 studio 의 422 를 같은 이름으로 쓸 수 없다.
# source: public-v1.yaml ApiError.code — PUBLIC_REQUEST_INVALID (PublicError.PUBLIC_REQUEST_INVALID)
- code: PUBLIC_REQUEST_INVALID
category: VALIDATION
http_status: 400
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-public-v1
owner_layer: application
client_safe_message: "요청 값이 올바르지 않습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: PublicErrorRegistryTest
# source: public-v1.yaml ApiError.code — PUBLIC_RESOURCE_NOT_FOUND (PublicError.PUBLIC_RESOURCE_NOT_FOUND)
- code: PUBLIC_RESOURCE_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-public-v1
owner_layer: application
client_safe_message: "요청한 자료를 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: PublicErrorRegistryTest
# ---------------------------------------------------------------------------
# TECH LOG STUDIO MANAGEMENT (studio-management-v1.yaml ApiError.code)
#
# ManagementError(dev.caskeleton.application.techlog.error.ManagementError)와 1:1.
# AUTHENTICATION_REQUIRED / STUDIO_ACCESS_DENIED / REQUEST_VALIDATION_FAILED /
# VERSION_CONFLICT 는 StudioError 에도 있어 행이 이미 존재한다 — 이 파일의 identity
# column 은 `code` 이므로 중복 행을 만들지 않는다.
# ---------------------------------------------------------------------------
# source: studio-management-v1.yaml ApiError.code — TOPIC_NOT_FOUND (ManagementError.TOPIC_NOT_FOUND)
- code: TOPIC_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "주제를 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — TOPIC_NAME_TAKEN (ManagementError.TOPIC_NAME_TAKEN)
- code: TOPIC_NAME_TAKEN
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "같은 이름의 주제가 이미 있습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — TOPIC_SLUG_TAKEN (ManagementError.TOPIC_SLUG_TAKEN)
- code: TOPIC_SLUG_TAKEN
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "같은 slug 의 주제가 이미 있습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — TOPIC_IN_USE (ManagementError.TOPIC_IN_USE)
- code: TOPIC_IN_USE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "이 주제를 쓰는 기록이 있어 삭제할 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — PROJECT_NOT_FOUND (ManagementError.PROJECT_NOT_FOUND)
- code: PROJECT_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "프로젝트를 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — PROJECT_SLUG_TAKEN (ManagementError.PROJECT_SLUG_TAKEN)
- code: PROJECT_SLUG_TAKEN
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "같은 slug 의 프로젝트가 이미 있습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — PROJECT_IN_USE (ManagementError.PROJECT_IN_USE)
- code: PROJECT_IN_USE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "이 프로젝트에 연결된 기록이 있어 삭제할 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — RELEASE_NOT_FOUND (ManagementError.RELEASE_NOT_FOUND)
- code: RELEASE_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "요청한 릴리즈를 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — RELEASE_VERSION_TAKEN (ManagementError.RELEASE_VERSION_TAKEN)
- code: RELEASE_VERSION_TAKEN
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "같은 버전의 릴리즈가 이미 있습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — RELEASE_NOT_PUBLISHABLE (ManagementError.RELEASE_NOT_PUBLISHABLE)
- code: RELEASE_NOT_PUBLISHABLE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "지금 상태에서는 발행할 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — DOCUMENT_PUBLISHED (ManagementError.DOCUMENT_PUBLISHED)
- code: DOCUMENT_PUBLISHED
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "공개된 기록은 삭제할 수 없습니다. 먼저 공개를 취소해 주세요"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — QUESTION_NOT_FOUND (ManagementError.QUESTION_NOT_FOUND)
- code: QUESTION_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "질문을 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — DOCUMENT_IN_USE (ManagementError.DOCUMENT_IN_USE)
- code: DOCUMENT_IN_USE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "이 기록을 참조하는 곳이 있어 삭제할 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — QUESTION_IN_USE (ManagementError.QUESTION_IN_USE)
- code: QUESTION_IN_USE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "이 질문을 참조하는 곳이 있어 삭제할 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — DECISION_NOT_FOUND (ManagementError.DECISION_NOT_FOUND)
- code: DECISION_NOT_FOUND
category: NOT_FOUND
http_status: 404
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "결정을 찾을 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest
# source: studio-management-v1.yaml ApiError.code — DECISION_IN_USE (ManagementError.DECISION_IN_USE)
- code: DECISION_IN_USE
category: CONFLICT
http_status: 409
retryable: false
retry_after_seconds: null
owner_branch: feature-techlog-management-v1
owner_layer: application
client_safe_message: "이 결정을 참조하는 곳이 있어 삭제할 수 없습니다"
log_level: INFO
runbook_link: null
compatibility_impact: additive
required_test: ManagementErrorRegistryTest