AwsS3Manager credential expiration not validated — missing checkToken logic
RCA: AwsS3Manager::uploadDirectoryByCredential | InvalidToken
Overview#
What Happened#
2026-06-02 23:53 KST, cupixworks-pano-postprocessor-instance 서비스(eu-central-1, hassan-allam 팀)에서 S3 업로드 시 InvalidToken 에러가 발생했다. AWS STS 임시 credential의 session token이 만료되거나 무효화된 상태에서 S3 API를 호출하여 HTTP 400 응답을 받았으며, 이후 30분간 동일 토큰으로 다운로드 작업도 실패했다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | InvalidToken (AWS S3 SDK) |
| exception.message | The provided token is malformed or otherwise invalid. |
| top_frame | aws-s3.manager.ts:130 (s3.upload) |
| runtime | Node.js (agents monorepo) |
| env | production, eu-central-1 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| hassan-allam (pano-postprocessor) | 8 (upload 4 + download 4) | 파노 타일 업로드/다운로드 실패, 처리 완료된 이미지가 S3에 저장되지 않음 |
Timeline#
- 2026-06-02 23:52 KST — 다수의 pano-postprocessor 인스턴스가 blur/infer/resize 처리 수행 중
- 2026-06-02 23:53:59 KST —
uploadDirectoryByCredential호출 시InvalidToken에러 발생 (4건) - 2026-06-03 00:04:10 KST — 동일 토큰으로
downloadParallel실패 (capture_37848) - 2026-06-03 00:23:25-42 KST — 추가 다운로드 실패 (capture_37811)
Error Log#
AwsS3Manager::uploadDirectoryByCredential | upload failed - {"message":"The provided token is malformed or otherwise invalid.","code":"InvalidToken","region":null,"time":"2026-06-02T14:53:59.145Z","requestId":"KNK1G9JKK8WAETZJ","extendedRequestId":"0UuQyr4OSdz5eaFQ8142FXXxkPrFIsm3DJQ6RKFTm7j2CSnUAJckmmjN0LLHTsp69cNwxkUnQ8jphfhbR3GoeoNJVUb9HSaz","statusCode":400,"retryable":false,"retryDelay":39.9095612733064}
Impact#
- Service:
cupixworks-pano-postprocessor-instance - Team: hassan-allam
- 발생 횟수: 3 (클러스터 기준, 실제 관련 에러 8건)
- 최초 발생: 2026-06-02 23:53 KST
- 최근 발생: 2026-06-02 23:53 KST
Root Cause Summary#
Pano-postprocessor의 AwsS3Manager는 credential을 사용 전에 만료 여부를 확인하는 checkToken() 로직이 없다. Base 패키지의 AwsS3Manager에는 _tokenExpiresAt 추적과 600초 버퍼를 둔 토큰 갱신 메커니즘이 구현되어 있으나, pano-postprocessor는 이를 상속하지 않고 독자적인 간소화된 구현을 사용한다. resize-work.ts에서 createTileUploadCredentials로 임시 credential을 발급받지만, 장시간 처리(blur_panos 17-21분 소요) 도중 또는 이후에 토큰이 만료되면 이를 감지하거나 갱신할 방법이 없다. 또한 에러 응답의 region: null은 S3 클라이언트 생성 시 region 미설정 문제도 시사한다.
Technical Analysis#
Code Path#
- Entry point:
resize-work.ts:138—uploadResizedImage메서드 - Credential 발급:
resize-work.ts:140—cupixApi.pano.createTileUploadCredentials(panoId)호출로 STS 임시 credential 획득 - S3 클라이언트 생성:
aws-s3.manager.ts:109-117— credential으로 새AWS.S3인스턴스 생성 (region 미설정) - Failure point:
aws-s3.manager.ts:130-134—s3.upload()호출 시 만료된 token으로 인해 InvalidToken 에러
uploadResizedImage = async (cpPano: CPPano): Promise<void> => {
if (!cpPano.panoId) return;
const credentials = await this.cupixApi.pano.createTileUploadCredentials(cpPano.panoId);
await this.awsS3Manager.uploadDirectoryByCredential({ credential: credentials, directory: cpPano.resizeDir!});
await this.cupixApi.pano.checkTileUploading(cpPano.panoId);
};
Credential 발급 직후 업로드를 수행하므로 정상적이라면 즉시 사용 가능해야 한다. 그러나 발급된 credential 자체가 무효(malformed)한 경우 — Tesla API에서 이미 만료된 STS 토큰을 반환했거나, Tesla API 인증 세션 문제로 잘못된 응답을 받았을 가능성이 있다.
async uploadDirectoryByCredential({ credential, directory }: { credential: TESLA.UploadCredentials, directory: string }): Promise<void> {
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
signatureVersion: 'v4',
accessKeyId: credential['aws_access_key_id'],
secretAccessKey: credential['aws_secret_access_key'],
sessionToken: credential['aws_session_token'],
endpoint: credential['endpoint'],
s3ForcePathStyle: true,
});
region 파라미터가 설정되지 않아 에러 응답에서 "region":null로 나타난다.
비교 — base 패키지의 토큰 갱신 로직:
private checkToken = async () => {
let isExpired = false;
if (this.tokenExpiresAt != undefined) {
const now = new Date().getTime();
const expiresTimeStamp = new Date(this.tokenExpiresAt).getTime();
isExpired = expiresTimeStamp - 600 * 1000 < now;
}
if (!isExpired || this._createCredentials == undefined) {
return;
}
const _credentials = await this._createCredentials();
this.setCredentials({
accessKeyId: _credentials.aws_access_key_id,
secretAccessKey: _credentials.aws_secret_access_key,
sessionToken: _credentials.aws_session_token,
expires_at: _credentials.expires_at,
endpoint: _credentials.endpoint
});
};
Base 구현은 매 업로드 chunk 전에 checkToken()을 호출하여 만료 600초 전에 자동 갱신한다. Pano-postprocessor는 이 패턴을 사용하지 않는다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-pano-postprocessor-instance status:error @environment:production "AwsS3Manager"
service:cupixworks-pano-postprocessor-instance status:error @environment:production "InvalidToken"
핵심 로그 — 업로드 실패:
{
"message": "AwsS3Manager::uploadDirectoryByCredential | upload failed - {\"message\":\"The provided token is malformed or otherwise invalid.\",\"code\":\"InvalidToken\",\"region\":null,\"time\":\"2026-06-02T14:53:59.145Z\",\"requestId\":\"KNK1G9JKK8WAETZJ\",\"statusCode\":400,\"retryable\":false}"
}
후속 다운로드 실패 (30분 후):
AwsS3Manager::downloadParallel | download failed - pano: /tmp/workspace/capture_37848/download/pano_8982256.jpg, error: The provided token is malformed or otherwise invalid.
AwsS3Manager::downloadParallel | download failed - pano: /tmp/workspace/capture_37811/download/pano_8986278.jpg, error: The provided token is malformed or otherwise invalid.
Info 레벨 로그에서 확인한 처리 시간:
blur_panos단계: 1000-1283초 (17-21분) 소요- 에러 발생 직전까지 다수 인스턴스가 정상적으로 authenticate → run → download → blur → infer → resize 수행 중
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Tesla API가 이미 만료된/무효한 STS credential을 반환 | 에러 메시지 "malformed or otherwise invalid" — 단순 만료가 아닌 토큰 자체 무효 시사; credential 발급 직후 사용했으므로 시간 만료 가능성 낮음; 동일 인스턴스에서 30분간 지속 실패 | 다른 인스턴스는 동시간대 정상 처리 완료 | Confirmed |
| H2 | 장시간 처리(blur 17-21분) 중 STS 토큰 자연 만료 | blur_panos 단계 장시간 소요 확인; base 패키지에 checkToken 존재는 이 문제 인지 의미 | uploadResizedImage에서 업로드 직전에 createTileUploadCredentials 호출하므로 fresh credential 사용; 에러 메시지가 "expired"가 아닌 "malformed" |
Rejected |
| H3 | S3 클라이언트에 region 미설정으로 인한 credential 검증 실패 | 에러 응답의 "region":null; S3 인스턴스 생성 시 region 파라미터 없음 |
AWS SDK는 region 없이도 endpoint 기반으로 동작 가능; 다른 시간대에는 동일 코드로 정상 동작 | Rejected |
| H4 | Tesla API 세션(CupixAuth) 만료로 credential 발급 API 호출 실패 → 잘못된 응답 수신 | 장시간 인스턴스 실행 시 세션 만료 가능; tesla-api.ts의 checkToken()이 API 호출 전 토큰 갱신하나 race condition 가능 |
명시적 API 호출 실패 로그 없음; createTileUploadCredentials 호출 자체가 예외 없이 완료된 것으로 추정 |
Inconclusive |
Fix Recommendation#
즉시 조치 (Critical)#
applications/agents/packages/cupix-pano-postprocessor/src/manager/aws-s3.manager.ts:108-151uploadDirectoryByCredential메서드에서 credential의expires_at필드를 확인하여, 만료 임박 시 (600초 이내) 업로드를 시도하지 않고 credential을 재발급받는 로직 추가- 또는 base 패키지의
checkToken()패턴을 도입하여setApiCreateCredentials콜백을 등록하고 매 chunk 업로드 전에 토큰 유효성 검증
단기 개선 (1주 이내)#
uploadDirectoryByCredential에서InvalidToken에러 발생 시 credential 재발급 후 재시도하는 retry 로직 구현- S3 클라이언트 생성 시
credential.bucket_region값을region파라미터로 전달하여 region null 문제 해소 - 에러 발생 시 현재는
logger.error만 하고 계속 진행하는데, 연속 실패 시 early return하여 불필요한 반복 시도 방지
장기 개선 (재발 방지)#
- Pano-postprocessor의
AwsS3Manager를 base 패키지의AwsS3Manager로 교체하거나 상속하여 일관된 credential 관리 패턴 적용 - 모든 agent 서비스에서 동일한 토큰 갱신 메커니즘을 사용하도록 통합
Monitoring#
InvalidToken에러 발생 시 즉시 알림:
service:cupixworks-pano-postprocessor-instance status:error "InvalidToken"
- Credential 만료 관련 메트릭 추가 — 발급 시점과 사용 시점 간의 시간 차이 추적
- 업로드 성공률 메트릭:
service:cupixworks-pano-postprocessor-instance "uploadDirectoryByCredential | end"
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard