ttapp v1.0.197 — Windows 버그 3종 수정 & ARM64 Linux 빌드 수복기
ttapp v1.0.197 — Windows 버그 3종 수정 & ARM64 Linux 빌드 수복기
ttapp 데스크톱 v1.0.197을 배포하면서 겪은 세 가지 버그와 해결 과정을 기록한다.
버그 1. Windows T봇 실행 시 "System prompt file not found"
증상
Windows에서 T봇을 실행하면 Claude CLI가 즉시 다음 오류를 뱉고 종료됐다.
System prompt file not found: C:\Users\...\AppData\Local\Temp\ttapp_sysprompt_1234567890.txt
macOS/Linux에서는 전혀 재현되지 않았다.
원인
ttapp 데스크톱은 T봇 실행 시 Claude CLI에 시스템 프롬프트를 전달하는데, Windows는 cmdline 길이 한계(32KB)가 있어서 --system-prompt-file 옵션으로 임시 파일을 만들어 전달하는 방식을 쓴다.
문제는 직전 커밋(v1.0.196)에서 "임시 파일 누적 방지"를 위해 파일 삭제 코드를 넣었는데, 삭제 타이밍이 spawn() 직후였다는 것이다.
// 문제 코드 (v1.0.196)
let child = command.spawn()?;
// ← Claude CLI가 아직 파일을 열지도 않았는데 여기서 삭제!
if let Some(ref p) = sys_prompt_tmp_path {
let _ = std::fs::remove_file(p);
}
spawn()은 프로세스를 생성하는 것이지, 프로세스가 파일을 열 때까지 기다려주지 않는다. 결국 Claude CLI가 파일을 열려고 하는 순간 이미 파일이 삭제된 상태가 되는 race condition이었다.
수정
파일 삭제를 child.wait() 완료 후로 이동했다.
// 수정 코드 (v1.0.197)
let child = command.spawn()?;
// spawn 직후에는 삭제하지 않음
// ... 프로세스 실행 중 ...
let status = child.wait().await?;
// 프로세스가 완전히 종료된 후 삭제
if let Some(ref p) = sys_prompt_tmp_path {
let _ = std::fs::remove_file(p);
}
이렇게 하면 Claude CLI가 프로세스 실행 내내 파일을 읽을 수 있고, 종료 후 깔끔하게 정리된다.
왜 macOS/Linux는 괜찮았나?
두 플랫폼은 cmdline 길이 제한이 충분히 크기 때문에 --system-prompt-file을 사용하지 않고 stdin으로 직접 전달한다. 파일 자체를 쓰지 않으니 이 버그의 영향을 받지 않았다.
버그 2. Windows 자동 업데이트 "signature verification failed"
증상
일부 Windows 사용자가 업데이트 시 다음 오류를 경험했다.
signature verification failed
이전에 발생했던 Invalid encoding in minisign data와는 다른 에러다.
Invalid encoding→ 서명 파일 자체가 손상된 경우signature verification failed→ 서명 포맷은 정상인데 암호학적 검증 실패
원인
배포 직후 타이밍 이슈였다.
- Firebase latest.json → v1.0.197 sig로 즉시 갱신됨
- GitHub Release MSI → CI 빌드 완료까지 수 분 소요
- 이 짧은 window에 업데이트를 시도한 사용자는 v1.0.197 sig + 아직 전파되지 않은 MSI를 만남
서명은 파일 내용을 기반으로 만들어지기 때문에, 다른 바이트의 파일을 검증하면 당연히 실패한다.
대책
배포 후 deploy.py --verify가 통과한 것을 확인한 뒤에 업데이트 안내를 하는 것으로 운영 절차를 정비했다.
버그 3. ARM64 Linux 빌드 "failed to run linuxdeploy"
증상
Ubuntu ARM64 VM에서 cargo tauri build --bundles appimage 실행 시:
Bundling ttapp_1.0.197_aarch64.AppImage
Error failed to bundle project `failed to run linuxdeploy`
원인 추적
linuxdeploy를 직접 실행해서 더 자세한 오류를 잡아냈다.
Deploying dependencies for ELF file ttapp.AppDir/usr/bin/ttapp-mcp
terminate called after throwing an instance of 'std::runtime_error'
what(): Failed to run ldd: exited with code 1
ttapp-mcp는 bun으로 크로스컴파일된 사이드카 바이너리다. linuxdeploy는 AppDir 내 모든 ELF 파일에 ldd를 실행해서 의존 라이브러리를 수집하는데, bun으로 컴파일된 바이너리는 실행 시 segfault가 발생해 ldd가 exit 1을 반환했다.
$ ldd ttapp-mcp
(아무 출력 없이 종료)
$ echo $?
1
$ ./ttapp-mcp
Segmentation fault (core dumped)
bun의 standalone binary는 V8 런타임을 통째로 내장한 self-contained 실행파일이라 ldd로 의존성을 분석하는 과정에서 crash가 발생한다.
수정
ldd가 실패했을 때 readelf -d로 대신 의존성을 읽어오는 래퍼 스크립트를 /usr/local/bin/ldd에 설치했다.
#!/bin/bash
REAL_LDD=/usr/bin/ldd
output=$("$REAL_LDD" "$@" 2>&1)
exitcode=$?
if [ $exitcode -eq 0 ]; then
echo "$output"
exit 0
fi
# 실패 시 readelf로 NEEDED 항목 추출
for f in "$@"; do
[ -f "$f" ] || continue
while IFS= read -r lib; do
found=$(ldconfig -p 2>/dev/null | grep " => " | grep "/$lib" | head -1 | awk "{print \$NF}")
# 표준 경로에서도 탐색
for d in /lib/aarch64-linux-gnu /usr/lib/aarch64-linux-gnu /lib /usr/lib; do
[ -z "$found" ] && [ -f "$d/$lib" ] && found="$d/$lib"
done
[ -n "$found" ] && printf "\t%s => %s (0x0)\n" "$lib" "$found" \
|| printf "\t%s => not found\n" "$lib"
done < <(readelf -d "$f" 2>/dev/null | grep NEEDED | grep -oP "(?<=\[)[^\]]+")
done
exit 0
이 래퍼가 /usr/local/bin/ldd에 설치되면 PATH 우선순위에 의해 linuxdeploy가 이걸 먼저 호출한다. bun 바이너리처럼 실행이 crash나는 경우에도 readelf로 정적 분석해서 의존성 목록을 반환하니 linuxdeploy가 정상적으로 진행할 수 있다.
결과
✅ darwin-aarch64: signature OK
✅ darwin-x86_64: signature OK
✅ linux-x86_64: signature OK (v1.0.197)
✅ linux-aarch64: signature OK (v1.0.197) ← 복구
✅ windows-x86_64: signature OK (v1.0.197)
전 플랫폼 검증 통과. 특히 ARM64 Linux는 이전 버전부터 sig가 stale 상태로 남아있었는데 이번에 완전히 정상화됐다.
세 버그 모두 "작은 변경이 예상치 못한 플랫폼에서 터진다"는 공통점이 있었다. 크로스플랫폼 앱 개발에서 플랫폼별 edge case를 잡는 것이 얼마나 중요한지 다시 한번 느꼈다.
이 글 공유하기
// SPONSORED
[>]댓글
아직 댓글이 없어요. 첫 댓글을 남겨보세요!