Skip to content

fix(js-sdk): don't treat "detached" in a branch name as a detached HEAD#1518

Open
anxkhn wants to merge 1 commit into
e2b-dev:mainfrom
anxkhn:loop/e2b__001
Open

fix(js-sdk): don't treat "detached" in a branch name as a detached HEAD#1518
anxkhn wants to merge 1 commit into
e2b-dev:mainfrom
anxkhn:loop/e2b__001

Conversation

@anxkhn

@anxkhn anxkhn commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

parseGitStatus in the JS SDK misreports a normal repository as being in detached HEAD state whenever the current branch or its upstream merely contains the substring detached.

Given a branch main tracking origin/detached-work, git status --porcelain=1 -b prints:

## main...origin/detached-work

The branch part is main...origin/detached-work, so the old substring check rawBranch.includes('detached') matched and the parser took the detached-HEAD path, returning detached: true and dropping currentBranch / upstream entirely.

Root cause

packages/js-sdk/src/sandbox/git/utils.ts:

const isDetached =
  rawBranch.startsWith('HEAD (detached at ') ||
  rawBranch.includes('detached')   // <- substring match, false positives

A real detached HEAD is reported by git as ## HEAD (no branch) or ## HEAD (detached at <sha>), never as the bare word detached inside an ordinary branch name. Those two real forms are already handled:

  • ## HEAD (detached at <sha>) -> rawBranch.startsWith('HEAD (detached at ').
  • ## HEAD (no branch) -> normalizeBranchName maps it to HEAD, caught by the existing normalizedBranch.startsWith('HEAD') branch.

So the includes('detached') clause can only ever produce false positives. This removes it:

const isDetached = rawBranch.startsWith('HEAD (detached at ')

Behavior change (before / after)

git status branch line before after
## main...origin/detached-work detached: true detached: false, currentBranch: 'main', upstream: 'origin/detached-work'
## feature/detached-session-fix detached: true detached: false, currentBranch: 'feature/detached-session-fix'
## HEAD (no branch) detached: true detached: true (unchanged)
## HEAD (detached at 1a2b3c4) detached: true detached: true (unchanged)

Usage example

import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create()

// A repo whose branch 'main' tracks 'origin/detached-work'.
const status = await sandbox.git.status('/path/to/repo')

// Before this fix: status.detached === true, currentBranch/upstream undefined.
// After this fix:
status.detached      // false
status.currentBranch // 'main'
status.upstream      // 'origin/detached-work'

Parity with the Python SDK

The repo guidelines ask that SDK changes be mirrored across JS and Python. This is the JS counterpart of the same defect tracked in #1373; the Python side is addressed in #1374 using the identical approach (drop the "detached" in raw_branch substring check). This PR brings the JS SDK to parity for the detached-HEAD case.

Tests

Adds packages/js-sdk/tests/git.parseStatus.test.ts (offline unit vitest project, no sandbox / API key) covering all four cases above: the two false positives are now detached: false with currentBranch / upstream preserved, and both real detached-HEAD forms still report detached: true.

pnpm --filter e2b exec vitest run --project unit tests/git.parseStatus.test.ts
# 4 passed

A changeset (e2b, patch) is included.

Checklist

  • pnpm run format (changed files pass prettier --check)
  • pnpm run lint (oxlint, clean)
  • pnpm run typecheck (clean)
  • Tests added and run (pnpm run test; new unit test 4/4)
  • Changeset added (pnpm changeset; e2b patch)
  • Usage example for the user-facing change (above)

parseGitStatus flagged detached: true whenever the porcelain branch line
contained the substring "detached", via `rawBranch.includes('detached')`.
A normal status line like `## main...origin/detached-work` (or a local
branch such as `feature/detached-fix`) was therefore misclassified as a
detached HEAD, and currentBranch/upstream were dropped.

Real detached HEADs print `## HEAD (no branch)` or
`## HEAD (detached at <sha>)`, which are already handled by the
`startsWith('HEAD (detached at ')` clause and the
`normalizedBranch.startsWith('HEAD')` branch, so the substring check only
ever produced false positives. Drop it.

This is the JS twin of the Python SDK fix for the same defect (e2b-dev#1373).
@cla-bot

cla-bot Bot commented Jun 30, 2026

Copy link
Copy Markdown

We require contributors to sign our Contributor License Agreement, and we don't have @anxkhn on file. You can sign our CLA at https://e2b.dev/docs/cla . Once you've signed, post a comment here that says '@cla-bot check'

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1b109c8c2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const isDetached =
rawBranch.startsWith('HEAD (detached at ') ||
rawBranch.includes('detached')
const isDetached = rawBranch.startsWith('HEAD (detached at ')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Apply the detached-status fix to the Python SDK too

Because this commit changes an SDK package, the root AGENTS.md requires equivalent JS and Python SDK changes. This line fixes only the JS parser, but packages/python-sdk/e2b/sandbox/_git/parse.py still has "detached" in raw_branch, and both sync and async Python git.status() paths call that parser, so Python users on a normal branch/upstream like main...origin/detached-work will still get detached=True with branch/upstream dropped while the changeset claims the defect is fixed.

Useful? React with 👍 / 👎.

@anxkhn

anxkhn commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@cla-bot check

@cla-bot cla-bot Bot added the cla-signed label Jul 1, 2026
@cla-bot

cla-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

The cla-bot has been summoned, and re-checked this pull request!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant