Fail to create salesforce Facility resource. reason: INVALID_CROSS_REFERENCE_KEY: invalid cross refe
RCA: Fail to create salesforce Facility resource - INVALID_CROSS_REFERENCE_KEY
Error Log#
Fail to create salesforce Facility resource. reason: INVALID_CROSS_REFERENCE_KEY: invalid cross reference id
RESPONSE: [{"message":"invalid cross reference id","errorCode":"INVALID_CROSS_REFERENCE_KEY","fields":[]}]
Impact#
- Service:
cupixworks-worker - 발생 횟수: 1
- 최초 발생: 2026-04-07T12:56:42.666Z
- 최근 발생: 2026-04-07T12:56:42.666Z
Root Cause Summary#
Facility ID 19748 ("St Francis BHU Job #1916 Project") 생성 시 Salesforce Project__c 레코드를 만들기 위해 Account__c 필드에 Team의 sf_resource_id를 전달했으나, 해당 값이 Salesforce 측에서 유효하지 않은 Account ID였기 때문에 INVALID_CROSS_REFERENCE_KEY 에러가 발생했습니다. base.rb:17의 blank? 검증은 값이 nil이거나 빈 문자열인 경우만 차단하므로, Team의 sf_resource_id가 존재하지만 Salesforce에서 삭제되었거나, 다른 org에 속하거나, 접근 권한이 없는 stale한 ID인 경우를 감지하지 못합니다. 이 에러는 7일간 전체 서비스에서 단 1회만 발생한 isolated incident입니다.
Technical Analysis#
Code Path#
- Entry point: Facility 생성 시
after_commit콜백이 트리거됩니다.
# app/models/concerns/salesforce_integratable/facility.rb:14
after_commit :create_salesforce_resource_in_worker, if: :create_salesforce_resource?, on: :create
-
create_salesforce_resource?는sf_resource_id.blank?를 확인하여 이미 Salesforce 레코드가 있는지 체크합니다 (facility.rb:16-18). -
Worker가 비동기로 실행됩니다:
# app/workers/salesforce_resource_create_worker.rb:5-9
def perform(model_id, model_type)
model = model_type.constantize.find(model_id)
facility_resource_id = Cupix::Salesforce::SalesforceService.create_resource!(model)
model.update!(sf_resource_id: facility_resource_id) if model.respond_to?(:sf_resource_id)
end
-
SalesforceService.create_resource!는 모델 타입에 따라 적절한 클라이언트로 라우팅합니다 (salesforce_service.rb:34-37). -
Facility Client가
Account__c에 Team의sf_resource_id를 매핑합니다:
# app/services/cupix/salesforce/client/facility.rb:10-11
def parent_resource_id(model)
model.team.sf_resource_id # Team의 Salesforce Account ID
end
# app/services/cupix/salesforce/client/facility.rb:14-21
def create_params(model)
{
Account__c: parent_resource_id(model), # 이 값이 유효하지 않음
Name: model.name,
Project_ID__c: model.id,
CupixWorks_Team_Domain__c: model.team.domain
}
end
- Failure point:
base.rb:17의blank?검증을 통과한 후 Salesforce API 호출에서 실패합니다:
# app/services/cupix/salesforce/client/base.rb:14-27
def create_resource!(model)
if parent_resource_id(model).blank?
Cupix::Logger.warn('Skip creating salesforce resource because parent_resource_id is blank.', ...)
return # nil/빈 문자열만 차단 — stale ID는 통과
end
begin
resource_id = @client.create!(sobject_name, create_params(model))
rescue => e
Cupix::Logger.error("Fail to create salesforce #{model.class.name} resource. reason: #{e.message}", ...)
end
end
- 기대 동작: Team의
sf_resource_id가 Salesforce에서 유효한 Account ID이고,Project__c레코드가 정상 생성되어야 합니다. - 실제 동작: Team의
sf_resource_id가 비어 있지 않으나 Salesforce 측에서 유효하지 않은 ID로 인식되어INVALID_CROSS_REFERENCE_KEY에러가 반환되었습니다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-worker "Fail to create salesforce Facility resource" "INVALID_CROSS_REFERENCE_KEY"
핵심 로그 (1건):
{
"timestamp": "2026-04-07T12:56:42.666Z",
"service": "cupixworks-worker",
"status": "error",
"class": "Cupix::Salesforce::Client::Facility",
"function": "create_resource!",
"model_type": "Facility",
"model_id": 19748,
"model_name": "St Francis BHU Job #1916 Project",
"tenant": "cupix",
"host": "ip-10-1-18-233.us-west-2.compute.internal",
"message": "Fail to create salesforce Facility resource. reason: INVALID_CROSS_REFERENCE_KEY: invalid cross reference id",
"response": "[{\"message\":\"invalid cross reference id\",\"errorCode\":\"INVALID_CROSS_REFERENCE_KEY\",\"fields\":[]}]"
}
7일간 전체 서비스 대상 INVALID_CROSS_REFERENCE_KEY 검색:
"INVALID_CROSS_REFERENCE_KEY"
결과: 위 1건만 발견. 이 에러는 isolated incident입니다.
7일간 Salesforce 관련 에러 패턴 비교:
service:cupixworks-worker "salesforce" status:error
결과: 약 100건 이상 발견되었으나, 대부분은 FIELD_CUSTOM_VALIDATION_EXCEPTION: The Account on the Associated Opportunity and the Account on this Project Record Don't Match 에러(Account/Opportunity 불일치)이며, INVALID_CROSS_REFERENCE_KEY는 이 1건만 해당합니다.
이벤트 타임라인:
| 시간 | 이벤트 |
|---|---|
| 2026-04-07T12:56:42.666Z | Cupix::Salesforce::Client::Facility#create_resource! — Facility 19748 Salesforce 생성 실패 (INVALID_CROSS_REFERENCE_KEY) |
참고: Salesforce 응답의 fields 배열이 비어 있어, 어떤 필드가 invalid cross reference를 유발했는지 Salesforce가 명시하지 않았습니다. 코드 분석상 Account__c (= model.team.sf_resource_id)가 유일한 cross-reference 필드입니다.
Fix Recommendation#
즉시 조치 (Critical)#
- 파일:
app/services/cupix/salesforce/client/base.rb:17-20 parent_resource_id(model).blank?검증만으로는 stale/invalid Salesforce ID를 감지할 수 없습니다. Salesforce ID 형식 검증(15자 또는 18자 alphanumeric)을 추가하거나, Salesforce API로 ID 존재 여부를 사전 확인하는 로직을 고려해야 합니다.- 에러 발생 시 rescue 블록에서 에러를 삼키지 않고,
INVALID_CROSS_REFERENCE_KEY인 경우 재시도하거나 알림을 보내는 처리를 추가해야 합니다.
단기 개선 (1주 이내)#
- Facility 19748이 속한 Team의
sf_resource_id값을 확인하고, 해당 ID가 Salesforce에 실제로 존재하는지 검증해야 합니다. 만약 Salesforce 측에서 해당 Account가 삭제되었거나 재생성된 경우, Team의sf_resource_id를 갱신해야 합니다. base.rb:22-27의 rescue 블록에서 에러를 로그만 남기고 조용히 넘기고 있어,resource_id가 nil로 반환됩니다. Worker(salesforce_resource_create_worker.rb:9)는 이 nil 값을model.update!(sf_resource_id: nil)로 저장하여, Facility의sf_resource_id가 nil로 남게 됩니다. 에러 시 예외를 재발생시키거나 retry 메커니즘을 도입해야 합니다.
장기 개선 (재발 방지)#
- Team의 Salesforce Account 동기화 상태를 주기적으로 검증하는 health check를 도입하여,
sf_resource_id가 Salesforce에 존재하는지 확인하는 batch job을 추가합니다. - Salesforce 연동 에러를 유형별(
INVALID_CROSS_REFERENCE_KEY,FIELD_CUSTOM_VALIDATION_EXCEPTION등)로 분류하여 적절한 자동 복구 또는 알림 처리를 구현합니다.
Monitoring#
- Salesforce 연동 에러를 유형별로 모니터링:
service:cupixworks-worker "Fail to create salesforce" status:error
INVALID_CROSS_REFERENCE_KEY전용 알림:
service:cupixworks-worker "INVALID_CROSS_REFERENCE_KEY" status:error
- Facility
sf_resource_id가 nil인 레코드 수를 추적하여, Salesforce 동기화 실패율을 모니터링합니다.
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard
- 이 에러는 7일간 1회만 발생한 isolated incident로, 특정 Team의 stale Salesforce Account ID가 원인으로 추정됩니다. 다만,
base.rb의 에러 핸들링이 에러를 삼키는 패턴은 다른 Salesforce 연동 에러에도 공통적으로 적용되므로, 개선 시 전체 Salesforce 연동 안정성이 향상됩니다.