{"record":{"id":"670f0955fab0f42f","repo":"alibaba/open-code-review","slug":"resume-session-q-was-interrupted-before-it-closed","errorCode":null,"errorMessage":"resume session %q was interrupted before it closed, so it never recorded a run manifest and its input identity cannot be verified; %s","messagePattern":"resume session %q was interrupted before it closed, so it never recorded a run manifest and its input identity cannot be verified; (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/session/resume_identity.go","lineNumber":89,"sourceCode":"// validateInputIdentity compares only the input half of the resume contract: the\n// parent manifest must be verifiable, and every input field must match. Provider\n// and model are deliberately left out: those are command-line intent, not\n// something derived from the input.\n//\n// This runs once, at admission, and is never repeated during the run: the caller\n// pins the run to the commit endpoints this comparison was made against (see\n// agent.SealedInput), so a second comparison could only ever confirm the first.\nfunc (s *ResumeState) validateInputIdentity(id RunIdentity) error {\n\tif s == nil {\n\t\treturn nil\n\t}\n\n\tm := s.Manifest\n\tswitch {\n\tcase m == nil && !s.Closed:\n\t\t// Distinguishing all of these from \"manifest present, zero completed\n\t\t// items\" is the whole point: that one is resumable, none of these are.\n\t\treturn fmt.Errorf(\"resume session %q was interrupted before it closed, so it never recorded a run manifest and its input identity cannot be verified; %s\", s.SessionID, resumeHint)\n\tcase m == nil:\n\t\t// It closed cleanly, so blaming an interruption would send the user\n\t\t// looking for a crash that never happened. A session_end with no manifest\n\t\t// is a session older than run manifests, or a run that failed before\n\t\t// freezing one.\n\t\treturn fmt.Errorf(\"resume session %q closed without a run manifest, so its input identity cannot be verified — it either predates run manifests or failed before recording one; %s\", s.SessionID, resumeHint)\n\tcase m.SchemaVersion != ManifestSchemaVersion:\n\t\treturn fmt.Errorf(\"resume session %q carries manifest schema %q, but this build can only verify %q; %s\", s.SessionID, m.SchemaVersion, ManifestSchemaVersion, resumeHint)\n\tcase m.Operation != OperationReview:\n\t\treturn fmt.Errorf(\"resume session %q recorded operation %q, not %q; %s\", s.SessionID, m.Operation, OperationReview, resumeHint)\n\tcase len(m.Coverage.Selected) == 0:\n\t\t// Without this, an empty parent and an empty child would both hash to the\n\t\t// canonical empty digest, pass every comparison, and produce a run that\n\t\t// reuses nothing and dispatches nothing.\n\t\treturn fmt.Errorf(\"resume session %q selected no input, so it has nothing to resume; %s\", s.SessionID, resumeHint)\n\t}\n\n\tif m.Input.Mode != id.Mode {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/session/resume_identity.go#L71-L107","documentation":"validateInputIdentity refuses to resume a session that has no run manifest and was not closed cleanly. An interrupted session never froze its input identity, so there is nothing verifiable to resume against — resuming could silently mismatch the original input. The recommended action is to start a new review.","triggerScenarios":"Calling ValidateResume on a session where s.Manifest == nil and s.Closed == false — the process was killed/crashed between session start and session_end, before a manifest was persisted.","commonSituations":"Terminal or CI job killed mid-run (SIGKILL, OOM, timeout); power loss or crash during the first review run; the session was created but the run failed before the manifest freeze step.","solutions":["Start a new review instead of resuming (the hint appended to the error)","Delete or ignore the stale interrupted session and re-run the review from scratch","If crashes recur, fix the underlying interruption cause (timeout, OOM) before retrying"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Refuse to resume sessions that never completed a run\nif sess.Manifest == nil && !sess.Closed {\n    return fmt.Errorf(\"session %s was interrupted; start a new review instead\", sess.SessionID)\n}","typeGuard":"func resumableSession(s *session.State) bool {\n    return s != nil && s.Closed && s.Manifest != nil\n}","tryCatchPattern":"if err := ValidateResume(s, req); err != nil {\n    if strings.Contains(err.Error(), \"interrupted before it closed\") {\n        return startNewReview(req) // fall back to a fresh run\n    }\n    return err\n}","preventionTips":["Ensure the process persists the manifest promptly after session start","Handle SIGTERM/SIGINT so sessions close cleanly instead of being killed mid-run","Avoid resuming sessions from crashed/killed CI jobs; re-run from scratch"],"tags":["session","resume","interrupted","manifest"],"backgroundTag":"resume-unverifiable-session","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}