dolthub / dolthub/dolthub-issues

REST API: PR merge endpoint doesn't update PR state + case-sensitive auth header parsing

Open
#599 7 comments 0 reactions 2 assignees Claimed by @reltuk View on GitHub
dolthub api
Dominant language
No language data
Stars
5
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Summary

Two bugs in the DoltHub REST API (`https://www.dolthub.com/api/v1alpha1`) related to pull request management:

1. **`POST /pulls/{id}/merge` returns `Success` but leaves the PR state as `Open`** — the data is merged to the target branch, but the PR is never marked as "Merged". Waiting indefinitely does not help; the async job completes but the state never transitions.

2. **`PATCH /pulls/{id}` rejects `Content-Type` (capital C) with `"invalid authorization header"`** — the same request succeeds with `content-type` (lowercase). HTTP headers are case-insensitive per [RFC 7230 §3.2](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2), so the `authorization` header should be parsed regardless of other headers' casing.

## Steps to Reproduce

### Bug 1: Merge doesn't update PR state

```bash
# 1. Confirm PR is Open
curl -s "https://www.dolthub.com/api/v1alpha1/hop/wl-commons/pulls/166"
# → {"state": "Open", ...}

# 2. Merge via API
curl -s -X POST "https://www.dolthub.com/api/v1alpha1/hop/wl-commons/pulls/166/merge" \
-H "authorization: token $DOLTHUB_TOKEN"
# → {"status": "Success", "operation_name": "repositoryOwners/hop/repositories/wl-commons/jobs/...", ...}

# 3. Wait any amount of time, then check state
curl -s "https://www.dolthub.com/api/v1alpha1/hop/wl-commons/pulls/166"
# → {"state": "Open", ...} ← BUG: should be "Merged"
```

The data IS merged (the target branch's main contains the PR's commits), but the PR object never transitions to "Merged".

### Bug 2: Case-sensitive header parsing breaks auth

```bash
# With "Content-Type" (capital C) — FAILS
curl -s -X PATCH "https://www.dolthub.com/api/v1alpha1/hop/wl-commons/pulls/166" \
-H "authorization: token $DOLTHUB_TOKEN" \
-H "Content-Type: application/json" \
-d '{"state": "closed"}'
# → {"status": "Error", "message": "invalid authorization header"}

# With "content-type" (lowercase) — SUCCEEDS
curl -s -X PATCH "https://www.dolthub.com/api/v1alpha1/hop/wl-commons/pulls/166" \
-H "authorization: token $DOLTHUB_TOKEN" \
-H "content-type: application/json" \
-d '{"state": "closed"}'
# → {"status": "Success", "state": "Closed"}
```

Note: The `POST /merge` endpoint also exhibits this behavior — it only works without the `Content-Type` header or with lowercase `content-type`.

## Reproduction Script

```bash
#!/usr/bin/env bash
# Prerequisites: DOLTHUB_TOKEN, an open PR on a repo you have write access to
# Usage: DOLTHUB_TOKEN=dhat.v1.xxx UPSTREAM=org/repo PR_ID=123 bash repro.sh

set -euo pipefail
API="https://www.dolthub.com/api/v1alpha1"

echo "=== Verify PR is Open ==="
curl -s "$API/$UPSTREAM/pulls/$PR_ID" | python3 -c "import json,sys; print(f'State: {json.load(sys.stdin)[\"state\"]}')"

echo "=== Merge via API ==="
curl -s -X POST "$API/$UPSTREAM/pulls/$PR_ID/merge" -H "authorization: token $DOLTHUB_TOKEN" | python3 -m json.tool

echo "=== Wait 30s for async job ==="
sleep 30

echo "=== Check state (Bug 1: still Open) ==="
curl -s "$API/$UPSTREAM/pulls/$PR_ID" | python3 -c "import json,sys; print(f'State: {json.load(sys.stdin)[\"state\"]}')"

echo "=== PATCH with Content-Type (Bug 2: auth fails) ==="
curl -s -X PATCH "$API/$UPSTREAM/pulls/$PR_ID" -H "authorization: token $DOLTHUB_TOKEN" -H "Content-Type: application/json" -d '{"state":"closed"}' | python3 -m json.tool

echo "=== PATCH with content-type (works) ==="
curl -s -X PATCH "$API/$UPSTREAM/pulls/$PR_ID" -H "authorization: token $DOLTHUB_TOKEN" -H "content-type: application/json" -d '{"state":"closed"}' | python3 -m json.tool
```

## Expected Behavior

1. After `POST /pulls/{id}/merge` returns `Success`, the PR state should transition to `"Merged"`.
2. The `authorization` header should be parsed case-insensitively, regardless of the casing of other headers like `Content-Type`.
3. `PATCH /pulls/{id}` should accept `{"state": "merged"}` to allow programmatic merging with correct state.

## Actual Behavior

1. PR state remains `"Open"` indefinitely after a successful merge. The only way to close it is via `PATCH` with `{"state": "closed"}`, which shows "Closed" (not "Merged") in the DoltHub UI.
2. Including `Content-Type: application/json` (standard casing) causes `"invalid authorization header"` errors on both `POST /merge` and `PATCH` endpoints.
3. `PATCH` with `{"state": "merged"}` returns: `"This API can only be used to close a pull request, it cannot be used for merging or opening pull requests."`

## Impact

These bugs make it impossible to programmatically merge PRs via the REST API and have them display correctly in the DoltHub UI. The workaround is:
1. Merge data via `dolt` CLI (`dolt merge` + `dolt push`)
2. Close the PR via `PATCH` with lowercase `content-type`

This results in PRs showing as "Closed" instead of "Merged", which loses the merge provenance.

## Environment

- DoltHub REST API: `https://www.dolthub.com/api/v1alpha1`
- Auth: API token (`dhat.v1.*`)
- Tested against: `hop/wl-commons` (PRs #166, #167)
- Date: 2026-03-05

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.