Input file contains unsupported image format
RCA: Input file contains unsupported image format
Overview#
What Happened#
2026-06-03 13:36 KST에 cupixworks-any-floorplan-agent 서비스에서 floorplan 15711 처리 중 Sharp 라이브러리가 PDF 파일을 이미지로 읽으려다 실패했다. 원본 소스가 PDF인 경우, translateFloorplan 단계에서 출력 파일 경로가 .pdf 확장자를 유지하여 GraphicsMagick이 PNG 대신 PDF로 변환 결과를 출력했고, 이후 getImageResolution에서 Sharp가 PDF를 지원하지 않아 에러가 발생했다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Error |
| exception.message | Input file contains unsupported image format |
| top_frame | sharp/lib/input.js:487 → FloorplanService.getImageResolution (app.cjs:7029) |
| env | production, ap-southeast-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| endeavourgroup | 1 | floorplan 15711 처리 실패, 타일 생성 미완료 |
Timeline#
- 2026-06-03 13:36:29 KST — Floorplan 15711 처리 시작 (
BaseService::runByMessage) - 2026-06-03 13:36:29 KST — PDF 소스 다운로드 및
translateFloorplan시작 (출력:15711.pdf) - 2026-06-03 13:36:44 KST —
getImageResolution에서 Sharp가 PDF 입력을 거부하며 에러 발생 - 2026-06-03 13:36:44 KST — Workspace 정리 및 SQS 메시지 삭제 (작업 실패로 종료)
- 2026-06-03 13:36 KST — Error Sweeper 감지
Error Log#
Input file contains unsupported image format
Impact#
- Service:
cupixworks-any-floorplan-agent - Team: endeavourgroup
- 발생 횟수: 1
- 최초 발생: 2026-06-03 13:36 KST
- 최근 발생: 2026-06-03 13:36 KST
Root Cause Summary#
CPFloorplan 모델이 localFilePath를 결정할 때, floorplan의 name 속성에서 추출한 확장자가 ValidImageExtensions 목록(.pdf 포함)에 있으면 그대로 사용한다. 원본 소스가 PDF일 때 floorplan name도 .pdf 확장자를 가지므로, localFilePath가 /tmp/workspace/15711/15711.pdf가 된다. translateFloorplan은 이 경로를 GraphicsMagick의 output으로 전달하는데, GraphicsMagick은 출력 파일의 확장자에 따라 포맷을 결정하므로 PDF → PDF 변환이 수행된다. 이후 getImageResolution에서 Sharp가 이 PDF 파일의 metadata를 읽으려 하지만, Sharp는 PDF를 지원하지 않아 "Input file contains unsupported image format" 에러가 발생한다.
Technical Analysis#
Code Path#
- Entry point:
floorplan-service.ts:51—FloorplanService.run() cpfloorplan.ts:70-74—localFilePath결정 (확장자가.pdf이면 그대로 사용)floorplan-service.ts:150-181—translateFloorplan()에서 output path로.pdf경로 사용graphics-magick.process.ts:67— GM command에 출력 경로.pdf전달 → PDF 출력- Failure point:
floorplan-service.ts:384—getImageResolution()에서 Sharp가 PDF 읽기 실패
1. localFilePath 결정 로직:
const floorplanIdStr = this.id.toString();
const extractedExt = path.extname(this.name)?.toLowerCase();
const fileExt = Constants.ValidImageExtensions.includes(extractedExt)
? extractedExt
: Constants.DefaultImageFileFormat;
const localFileName = this._localFileName = floorplanIdStr + fileExt;
ValidImageExtensions에 .pdf가 포함되어 있으므로, floorplan name이 something.pdf이면 fileExt = '.pdf'가 되고, localFilePath는 /tmp/workspace/15711/15711.pdf가 된다.
2. ValidImageExtensions 정의:
export const DefaultImageFileFormat = '.png';
export const ValidImageExtensions = ['.png', '.jpg', '.jpeg', '.gif', '.tiff', '.pdf'];
.pdf가 "valid image extension"으로 포함되어 있어 PDF 소스의 확장자가 보존된다.
3. translateFloorplan에서 output 경로 전달:
const source = cpFloorplan.cpFloorplanSource.localFilePath;
const output = cpFloorplan.localFilePath;
logger.info('FloorplanService::translateFloorplan | source: %s, frame: %d, output: %s, width: %d, height: %d, dpi: %d, LOD: %d'
, source, frame, output, width, height, dpi, tileLod);
await this.graphicsMagickManager.execute({
input_file_path: source,
output_image_path: output,
resized_width: width,
resized_height: height,
frame: frame,
quality: Constants.DefaultQuality,
density: dpi
});
output이 15711.pdf이므로 GraphicsMagick은 PDF 형식으로 출력한다.
4. GraphicsMagick 명령어 구성:
if (source.toLowerCase().indexOf('pdf') > -1) {
command += ' -define "pdf:use-cropbox=true"';
command += ` ${source}[${frame}]`;
} else {
command += ` ${source}`;
}
command += ` ${output}`;
최종 명령어: gm convert ... 8004.pdf[0] 15711.pdf → PDF-to-PDF 변환 (이미지 변환이 아님).
5. getImageResolution 실패 지점:
private getImageResolution = (filePath?: string): Promise< number[] > => new Promise((resolve, reject) => {
if (filePath == undefined) {
return reject('FloorplanService::getImageResolution - undefined filePath');
}
logger.debug('FloorplanService::getImageResolution | begin');
const sharpOptions = { limitInputPixels: false };
sharp(filePath, sharpOptions)
.metadata((error, meta) => {
if (error) {
logger.debug('FloorplanService::getImageResolution | failed to get image size - %s', error.message.toString());
reject(error);
} else if (meta.width != undefined && meta.height != undefined) {
logger.debug('FloorplanService::getImageResolution | end - [%d, %d]', meta.width, meta.height);
resolve([meta.width, meta.height]);
}
});
});
filePath가 15711.pdf이므로 Sharp가 PDF 파일을 열려다 "Input file contains unsupported image format" 에러를 throw한다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-any-floorplan-agent status:error @environment:production "Input file contains unsupported image format"
service:cupixworks-any-floorplan-agent @floorplan.id:15711
실행 흐름 로그 (floorplan 15711):
04:36:29.373Z [info] BaseService::runByMessage | id: 15711
04:36:29.431Z [info] CupixAuth::setSession | session_id: 7d1d1e220389ed9b98af885aae04083c3e50fb92
04:36:29.471Z [info] FloorplanService::run | floorplan state: created, resource_state: created
04:36:29.509Z [info] FloorplanService::run | cpFloorplanSource_downloadUrl: http://api-tesla.cupix.internal/api/v1/floorplan_sources/8004/download
04:36:29.956Z [info] FloorplanService::translateFloorplan | source: /tmp/workspace/15711/source/8004.pdf, frame: 0, output: /tmp/workspace/15711/15711.pdf, width: 16384, height: 16384, dpi: 300, LOD: 6
04:36:44.053Z [error] Input file contains unsupported image format
04:36:44.053Z [info] BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/15711
04:36:44.058Z [info] AwsQueueManager::deleteMessage | begin
04:36:44.090Z [info] AwsQueueManager::deleteMessage | end
핵심 증거: translateFloorplan 로그에서 output이 15711.pdf인 것을 확인. 정상 동작하는 floorplan 15722의 경우 output이 15722.png으로 기록됨.
Stack trace:
Error: Input file contains unsupported image format
at Sharp.metadata (/tmp/agent/dist/node_modules/.pnpm/sharp@0.33.5/node_modules/sharp/lib/input.js:487:17)
at /tmp/agent/dist/app.cjs:7035:57
at new Promise (<anonymous>)
at FloorplanService.getImageResolution (/tmp/agent/dist/app.cjs:7029:45)
at FloorplanService.run (/tmp/agent/dist/app.cjs:6780:41)
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | PDF 확장자가 output path에 유지되어 GM이 PDF로 출력, Sharp가 PDF를 읽지 못함 | translateFloorplan 로그에서 output이 15711.pdf, cpfloorplan.ts:71에서 .pdf가 ValidImageExtensions에 포함, Sharp는 PDF 미지원 |
— | Confirmed |
| H2 | 소스 PDF 파일 자체가 손상되어 GM 변환 실패 | — | GM 변환은 성공적으로 완료됨 (에러 없이 14초 후 다음 단계로 진행), 에러는 GM이 아닌 Sharp에서 발생 | Rejected |
| H3 | Sharp 버전 호환성 문제로 정상 이미지도 읽지 못함 | — | 동일 시간대 다른 floorplan(15722)은 정상 처리됨, 에러 메시지가 "unsupported format"으로 포맷 문제를 명시 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
src/model/cpfloorplan.ts:71-73:localFilePath결정 시 PDF 확장자를 이미지 확장자(.png)로 강제 변환해야 한다.ValidImageExtensions에서.pdf를 제거하거나, PDF인 경우DefaultImageFileFormat(.png)으로 fallback하는 로직 추가.- 또는
src/floorplan-service.ts:166:translateFloorplan에서 output path의 확장자가.pdf인 경우.png로 교체하는 로직 추가.
단기 개선 (1주 이내)#
ValidImageExtensions목록을 "소스 파일 검증용"과 "출력 파일 포맷용"으로 분리. PDF는 유효한 소스 포맷이지만, 출력 포맷으로는 사용되면 안 된다.getImageResolution호출 전에 파일이 Sharp 지원 포맷인지 사전 검증하는 guard 추가.
장기 개선 (재발 방지)#
translateFloorplan의 output 경로를 항상 이미지 포맷(PNG)으로 강제하는 아키텍처 변경. 소스 포맷과 출력 포맷을 명확히 분리하는 설계 원칙 적용.- PDF 소스에 대한 통합 테스트 추가 (PDF → PNG 변환 → Sharp metadata 읽기 → 타일링 전체 파이프라인 검증).
Monitoring#
- Floorplan agent에서 PDF 소스 처리 시 output 확장자를 로깅하는 메트릭 추가
- Datadog 쿼리:
service:cupixworks-any-floorplan-agent status:error "unsupported image format"
service:cupixworks-any-floorplan-agent "translateFloorplan" "output:" ".pdf"
Risk Assessment#
- Risk level: medium
- 예상 복잡도: trivial —
cpfloorplan.ts에서 PDF 확장자 처리 로직 1-2줄 수정으로 해결 가능. 다만 PDF 소스 floorplan이 특정 팀에서만 사용되므로 영향 범위는 제한적이나, 해당 팀의 모든 PDF floorplan 업로드가 실패할 수 있음.