Skip to content

Add error handling for runInit in container entrypoint#35

Merged
gricha merged 1 commit intomainfrom
fix-init-error-handling
Jan 7, 2026
Merged

Add error handling for runInit in container entrypoint#35
gricha merged 1 commit intomainfrom
fix-init-error-handling

Conversation

@gricha
Copy link
Copy Markdown
Owner

@gricha gricha commented Jan 7, 2026

Summary

Addresses Sentry review feedback from PR #34.

The runInit() call in the container entrypoint lacked error handling. If initialization failed (e.g., git clone failure, bootstrap script error), the container would crash and restart infinitely due to the unless-stopped restart policy, preventing SSH access for debugging.

Changes

  • Wrapped runInit() in try/catch block
  • Errors are logged as non-fatal
  • SSH daemon still starts so users can connect and debug initialization issues

Test plan

  • Existing clone test passes with new image
  • bun run check passes

🤖 Generated with Claude Code

Comment on lines +23 to +28
try {
await runInit();
} catch (error) {
console.log(`[entrypoint] Initialization failed (non-fatal): ${(error as Error).message}`);
console.log("[entrypoint] SSH will still start - connect to debug the issue");
}

This comment was marked as outdated.

Addresses Sentry review feedback on PR #34. If runInit() throws
(e.g., git clone fails), the container would crash and restart
infinitely, preventing SSH access for debugging.

Now initialization failures are caught and logged as non-fatal,
allowing the SSH daemon to start so users can connect and debug.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@gricha gricha force-pushed the fix-init-error-handling branch from f7b1b2a to cd9d48f Compare January 7, 2026 19:55
Comment on lines +86 to +93
const repoName = repoUrl.replace(/\/+$/, "").split("/").pop() ?? "repo";
const cleanRepoName = repoName.replace(/\.git$/, "");
const repoPath = path.join(workspaceHome, cleanRepoName);
if (await pathExists(repoPath)) {
console.log(`Repository directory '${cleanRepoName}' already exists. Skipping clone.`);
await configureGitSshKey(workspaceHome, repoPath, runtimeConfig?.ssh?.selectedKey ?? "");
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: A new check for an existing repository directory causes an infinite initialization loop if a post-clone step fails, preventing the workspace from ever successfully starting up.
Severity: CRITICAL | Confidence: High

🔍 Detailed Analysis

A new check for an existing repository directory can lead to an infinite initialization loop. If a post-clone step, such as runBootstrapScripts(), fails, the .workspace-initialized marker file is not created. On subsequent restarts, the new check at lines 86-93 will find the existing repository directory and cause cloneRepository() to return early. The initialization process will then re-run the subsequent steps, which will fail again at the same point. This leaves the workspace in a permanently broken state, unable to complete initialization without manual user intervention to remove the repository directory.

💡 Suggested Fix

The logic should be adjusted to handle this semi-initialized state. Instead of returning early if the repository directory exists, consider either deleting the directory to allow a fresh clone or making the subsequent initialization steps more robust to this condition. The previous behavior, which would fail on the git clone command itself, was more explicit.

🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: perry/internal/src/commands/init.ts#L86-L93

Potential issue: A new check for an existing repository directory can lead to an
infinite initialization loop. If a post-clone step, such as `runBootstrapScripts()`,
fails, the `.workspace-initialized` marker file is not created. On subsequent restarts,
the new check at lines 86-93 will find the existing repository directory and cause
`cloneRepository()` to return early. The initialization process will then re-run the
subsequent steps, which will fail again at the same point. This leaves the workspace in
a permanently broken state, unable to complete initialization without manual user
intervention to remove the repository directory.

Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 8300857

@gricha
Copy link
Copy Markdown
Owner Author

gricha commented Jan 7, 2026

Re: Sentry's second comment about "infinite initialization loop":

The current behavior is intentional graceful degradation:

  1. Clone succeeds, bootstrap fails → Error is caught in entrypoint → SSH daemon starts → User can SSH in and debug
  2. On restart → Clone is skipped (directory exists) → Bootstrap retries → If fails again, SSH still starts

This is better than the alternatives:

  • ❌ Deleting the repo on failure would lose user's local changes
  • ❌ Re-cloning every time would be slow and might fail for the same reason
  • ✅ Current approach: Skip clone, retry bootstrap, always ensure SSH access

The workspace isn't "broken" - it's in a recoverable state where users can SSH in to diagnose and fix the bootstrap issue.

@gricha gricha merged commit d45ce19 into main Jan 7, 2026
9 of 11 checks passed
@gricha gricha deleted the fix-init-error-handling branch January 7, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant