{"record":{"id":"af73ee8288102883","repo":"alibaba/open-code-review","slug":"resume-rejected-the-reviewed-input-changed-since","errorCode":null,"errorMessage":"resume rejected: the reviewed input changed since session %q — a ref may now point at a different commit, or the selected file set changed; %s","messagePattern":"resume rejected: the reviewed input changed since session %q — a ref may now point at a different commit, or the selected file set changed; (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/session/resume_identity.go","lineNumber":117,"sourceCode":"\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 {\n\t\t// Mode feeds item_id derivation, so parent and child items cannot even be\n\t\t// put side by side.\n\t\treturn fmt.Errorf(\"resume rejected: input mode changed from %q to %q; %s\", m.Input.Mode, id.Mode, resumeHint)\n\t}\n\t// Both sides empty means a repository with no remote, which is unchanged.\n\tif m.Repository.IdentitySHA256 != id.RepositorySHA256 {\n\t\treturn fmt.Errorf(\"resume rejected: repository identity changed, so this is not the repository the parent run reviewed; %s\", resumeHint)\n\t}\n\tif m.Input.SourceArtifactSHA256 != id.SourceArtifactSHA256 {\n\t\treturn fmt.Errorf(\"resume rejected: the reviewed input changed since session %q — a ref may now point at a different commit, or the selected file set changed; %s\", s.SessionID, resumeHint)\n\t}\n\tif m.Execution.RuleConfigSHA256 == \"\" {\n\t\treturn fmt.Errorf(\"resume session %q recorded no rule identity, so it cannot be verified against the current rules; %s\", s.SessionID, resumeHint)\n\t}\n\tif m.Execution.RuleConfigSHA256 != id.RuleConfigSHA256 {\n\t\t// The digest is one aggregate, so it can only be attributed to a layer,\n\t\t// never to a specific rule or pattern.\n\t\treturn fmt.Errorf(\"resume rejected: review rule identity changed — either a rule text layer (custom, project, global or system) or the include/exclude file filter differs from session %q; %s\", s.SessionID, resumeHint)\n\t}\n\treturn nil\n}\n\n// explicitFlagHint renders the actionable half of a transition rejection. value\n// is empty whenever the endpoint has no provider name — one configured straight\n// from environment variables has none — and `pass --provider ` is not a command\n// anyone can run, so name the flag rather than echoing the empty value.\nfunc explicitFlagHint(flag, value string) string {\n\tif value == \"\" {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/session/resume_identity.go#L99-L135","documentation":"validateInputIdentity rejects resuming when the digest of the reviewed input (SourceArtifactSHA256) differs between the parent manifest and the current request. This means the actual reviewed content changed since the session — e.g. a ref now resolves to a different commit or the selected file set differs — so prior results cannot be safely reused.","triggerScenarios":"ValidateResume where m.Input.SourceArtifactSHA256 != id.SourceArtifactSHA256 — new commits landed on the reviewed branch, files changed, or the selection was computed differently between the parent run and the resume.","commonSituations":"Developer pushes new commits to a PR after starting a review, then resumes; CI re-runs after the branch moved; a mutable ref (e.g. a branch name instead of a SHA) resolves elsewhere at resume time.","solutions":["Pin the input to the original commit SHA (the one recorded in the parent manifest) and resume","Start a new review against the updated input instead of resuming","If resuming should reflect new commits, accept that identity changed and run a fresh review"],"exampleFix":"// before\nocr review --resume abc123 --ref main        # main moved since parent run\n// after\nocr review --resume abc123 --ref abcdef1234  # pin the original commit, then resume","handlingStrategy":"validation","validationCode":"// Confirm the reviewed input is unchanged before resuming\nif sess.Manifest != nil && sess.Manifest.Input.SourceArtifactSHA256 != currentSourceArtifactSHA256 {\n    return fmt.Errorf(\"input changed since parent run; start a new review\")\n}","typeGuard":null,"tryCatchPattern":"if err := ValidateResume(s, req); err != nil {\n    if strings.Contains(err.Error(), \"reviewed input changed\") {\n        return startNewReview(req) // branch moved — re-review the new state\n    }\n    return err\n}","preventionTips":["Pin refs to commit SHAs, not branch names, for resumable runs","Avoid pushing new commits to a PR between the parent review and the resume","Recompute the artifact digest before resuming to fail fast in scripts"],"tags":["session","resume","input","git"],"backgroundTag":"resume-state-mismatch","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}