{"record":{"id":"b92efc068401cf44","repo":"can1357/oh-my-pi","slug":"ref-diff-security-plan-is-missing-resolved-revisio","errorCode":null,"errorMessage":"ref_diff security plan is missing resolved revisions","messagePattern":"ref_diff security plan is missing resolved revisions","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/security/coordinator.ts","lineNumber":341,"sourceCode":"\t\treturn await repo.worktreeRemove(cwd, true);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nasync function prepareSecurityExecutionTarget(\n\tplan: SecurityScanPlan,\n\tstore: SecurityStore,\n\tscanId: string,\n\tadapter: SecurityGitAdapter,\n\tsignal: AbortSignal,\n): Promise<PreparedSecurityExecutionTarget> {\n\tif (plan.target.kind !== \"ref_diff\") {\n\t\treturn { cwd: plan.repositoryRoot, cleanup: async () => undefined };\n\t}\n\tconst headRevision = plan.target.headRevision;\n\tconst baseRevision = plan.target.baseRevision;\n\tif (!headRevision || !baseRevision) throw new Error(\"ref_diff security plan is missing resolved revisions\");\n\tconst targetsRoot = path.join(store.projectDirectory, \"targets\");\n\tawait fs.mkdir(targetsRoot, { recursive: true, mode: 0o700 });\n\tif (process.platform !== \"win32\") await fs.chmod(targetsRoot, 0o700);\n\tconst cwd = path.join(targetsRoot, scanId);\n\tconst repo = vcs.requireGit(plan.repositoryRoot);\n\tlet added = false;\n\ttry {\n\t\tawait repo.worktreeAdd(cwd, headRevision, true, signal);\n\t\tadded = true;\n\t\tconst diffText = await adapter.diffTree(plan.repositoryRoot, baseRevision, headRevision, signal);\n\t\treturn {\n\t\t\tcwd,\n\t\t\tdiffText,\n\t\t\tasync cleanup() {\n\t\t\t\tconst removed = await tryRemoveWorktree(repo, cwd);\n\t\t\t\tif (!removed) await fs.rm(cwd, { recursive: true, force: true });\n\t\t\t},\n\t\t};","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/security/coordinator.ts#L323-L359","documentation":"prepareSecurityExecutionTarget only handles ref_diff plans whose target carries fully resolved headRevision and baseRevision git refs; these are set during plan creation by resolving the diff range. If either is empty at execution time the plan is malformed or was produced by an older/newer plan format, so the coordinator refuses to build the worktree and diff.","triggerScenarios":"Calling coordinator.start() with a ref_diff plan whose target.headRevision or target.baseRevision is empty/undefined — e.g. a plan loaded from a store written before revisions were resolved, a plan created while HEAD/base could not be resolved, or a hand-constructed SecurityScanPlan passed directly to #run paths.","commonSituations":"Running a scan against a repository with no commits or an unborn HEAD; plan persisted by a different version of the tool with an incompatible plan schema; constructing a plan programmatically without going through preflight/createSecurityScanPlan.","solutions":["Create a fresh plan via coordinator.preflight() so revisions are resolved against the current repository state, then start from that plan id.","Verify the repository has valid HEAD and base refs (git rev-parse HEAD and the base revision) before scanning a ref_diff target.","Delete stale persisted plans and regenerate; check for a version mismatch if plans came from an older release.","If constructing plans in code, use createSecurityScanPlan rather than building the target object manually."],"exampleFix":"// before\nconst plan = await store.getPlan(oldPlanId); // pre-resolution plan\nawait coordinator.start({ planId: oldPlanId });\n// after\nconst plan = await coordinator.preflight({ target: { kind: \"ref_diff\", baseRevision: \"main\" } });\nawait coordinator.start({ planId: plan.id });","handlingStrategy":"validation","validationCode":"if (plan.target.kind === \"ref_diff\") {\n  const { headRevision, baseRevision } = plan.target;\n  if (!headRevision || !baseRevision) throw new Error(\"ref_diff target missing revisions; re-plan\");\n}","typeGuard":"function hasResolvedRevisions(target: SecurityScanPlan[\"target\"]): target is Extract<typeof target, { kind: \"ref_diff\"; headRevision: string; baseRevision: string }> {\n  return target.kind !== \"ref_diff\" || (Boolean(target.headRevision) && Boolean(target.baseRevision));\n}","tryCatchPattern":"try {\n  await coordinator.start({ planId });\n} catch (err) {\n  if (err instanceof Error && err.message === \"ref_diff security plan is missing resolved revisions\") {\n    const plan = await coordinator.preflight({ target: desiredTarget });\n    await coordinator.start({ planId: plan.id });\n  } else throw err;\n}","preventionTips":["Always create ref_diff plans through preflight()/createSecurityScanPlan so revisions get resolved.","Re-plan after pulling/branching so resolved refs still exist.","Treat persisted plans as ephemeral: create, start, and discard within one run."],"tags":["security-scan","git","plan-validation"],"backgroundTag":"missing-resolved-revisions","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}