작업 스케줄러 결과 코드 267014, 성공 0 인데도 안 된 경우

세 줄 요약

겪은 일: 긴 작업이 매일 중간에 멈추는데 오류가 없거나, 결과 코드는 0 인데 실제로는 백업이 몇 시간째 멈춰 있습니다.

원인: 실행 시간 제한 기본값 3시간이 작업을 강제 종료하거나, 스크립트 안의 조기 종료 길이 return 0 으로 중요한 단계를 건너뜁니다.

이렇게 풀었다: ExecutionTimeLimit 를 PT0S 로 풀고, 조기 종료 길도 밀린 일을 처리하게 바꾸고, 판정은 도착지에서 합니다.

작업 스케줄러의 "마지막 실행 결과"는 편리하지만, 그 숫자만 보고 판단하면 두 번 속습니다. 한 번은 숫자가 무엇을 뜻하는지 몰라서, 한 번은 숫자가 맞는데도 결과가 안 가서였습니다.

오류 없이 멈추고, 성공인데도 멈췄다

첫 번째 경우는 몇 시간 걸리는 작업이 매일 중간에 멈춘 것입니다. 스크립트 로그에는 오류가 없었고, PC 켜지면 자동으로 다시 도는 옵션(StartWhenAvailable)도 켜 두었는데 다시 돌지 않았습니다.

두 번째 경우는 15분마다 도는 백업 작업입니다. 원격 저장소의 마지막 기록은 05:00 에 멈춰 있었는데, 스케줄러는 10:00 까지 15분마다 정상으로 돌았고 결과 코드는 매번 0 이었습니다. 확인해 보니 커밋 6개가 올라가지 못하고 밀려 있었습니다.

실행 시간 제한 3시간

작업 스케줄러의 실행 시간 제한(ExecutionTimeLimit)은 기본값이 3시간(PT3H)입니다. 이보다 오래 걸리면 윈도우가 작업을 강제로 끝내고, 스크립트 쪽에는 아무 기록도 남지 않습니다. StartWhenAvailable 은 예정 시각을 놓쳤을 때 보충하는 기능이라, 정상으로 시작했다가 강제 종료된 경우에는 발동하지 않습니다.

작업이 매번 처음부터 다시 시작하는 구조라면 더 나쁩니다. 3시간에 끊기고 다음 날 또 앞부분만 반복하며 영원히 끝나지 않습니다.

return 0 으로 끝나는 조기 종료 길

백업 스크립트에는 일찍 끝나는 길이 둘 있었습니다. 바뀐 파일이 없을 때, 그리고 마지막 커밋이 최소 간격보다 최근일 때입니다. 둘 다 return 0 으로 끝나며 push 를 건너뛰었습니다.

사람이 손으로 커밋을 하면 이 두 조건에 동시에 걸려서, 밀린 커밋이 영영 올라가지 않습니다. 프로그램도 스케줄러도 틀린 말을 하지 않았는데 결과만 가지 않은 것입니다.

LastTaskResult 16진수 뜻
0 0x0 성공(안쪽 단계가 실제로 됐는지는 모름)
267009 0x41301 실행 중
267011 0x41303 실행된 적 없음
267014 0x41306 강제 종료됨

1. 결과 코드부터 읽기

Get-ScheduledTaskInfo -TaskName "LongJob" | Select-Object LastRunTime, LastTaskResult

267014 가 보이면 실행 시간 제한에 걸린 것입니다.

2. 실행 시간 제한 풀기

$t = Get-ScheduledTask -TaskName "LongJob"
$t.Settings.ExecutionTimeLimit = 'PT0S'
Set-ScheduledTask -InputObject $t

함께 작업을 이어 하기 구조로 바꿔 둡니다. 어디까지 했는지 기록하고 다음 실행은 그 뒤부터 하게 하면, 어떤 이유로 끊겨도 앞부분을 반복하지 않습니다.

새 작업 등록은 관리자 권한이 필요합니다. 권한이 없을 때는 시작프로그램 폴더에 창을 숨긴 VBS 를 두는 방법으로 대신했습니다.

3. 모든 조기 종료 길이 밀린 일을 거치게

def push_pending():
    ahead = run(["git", "rev-list", "--count", "origin/main..HEAD"])
    if int(ahead) > 0:
        run(["git", "push"])
        log("밀려 있던 커밋을 밀어 올렸다")

def main():
    if not has_changes():
        push_pending()
        return 0
    if too_soon_since_last_commit():
        push_pending()
        return 0
    commit()
    push_pending()
    return 0

위는 구조를 보여 주는 예시입니다. 핵심은 push 를 한 함수로 모으고 모든 return 앞에서 그 함수를 부르는 것입니다.

고친 뒤 확인할 것

백업이나 동기화가 도는지는 결과 코드나 로그 시각이 아니라 도착지에서 봅니다. 원격 저장소라면 마지막 커밋의 시각을 직접 읽습니다.

git --git-dir /srv/backup.git log -1 --format="%ci %s"

고친 뒤에는 일부러 커밋 1개를 손으로 밀어 놓고 작업을 돌렸습니다. 로그에 "밀려 있던 커밋을 밀어 올렸다"가 찍히고 도착지 시각이 바뀌는 것을 보고 나서야 끝났다고 봤습니다. 시간 제한 쪽은 다음 날 LastTaskResult 가 267014 가 아닌지 다시 확인합니다.

두 경우의 공통점

두 사건 모두 "아무도 틀린 말을 하지 않았다"는 점이 같습니다. 시간 제한은 설정대로 작업을 끝냈고, 백업 스크립트는 할 일이 없다고 판단해 정상 종료했습니다. 스케줄러는 그 결과를 그대로 보여 줬을 뿐입니다.

그래서 결과 코드는 "프로그램이 어떻게 끝났는가"만 알려 준다고 생각하는 편이 안전합니다. 실제로 원하는 일이 일어났는지는 그 일이 남기는 흔적, 곧 도착지의 파일이나 커밋, 작업이 처리한 개수에서 따로 확인해야 합니다. 이번 백업 문제도 다른 감시가 옛 상태를 계속 알리는 것을 이상하게 여겨 파고들다가 우연히 찾았습니다.

확인한 환경

항목 내용
운영체제 윈도우 PC, 작업 스케줄러
작업 몇 시간 걸리는 로컬 배치, 15분 간격 git 백업 스크립트

관련 글: 리눅스 크론이 돈다고 찍히는데 실제로는 안 돈 경우, 로컬 AI 장시간 작업이 30분 만에 죽을 때

이 글의 내용은 2026년 10월 5일에 마지막으로 확인했습니다. 틀린 곳을 발견하시면 연락처로 알려 주세요.