gastownhall/beads · error

failed to parse create response: %w

Error message

failed to parse create response: %w

What it means

CreateIssue received a response it could not decode into a single Issue object. The HTTP call succeeded but the body was not the expected JSON issue payload.

Source

Thrown at internal/gitlab/client.go:358

// CreateIssue creates a new issue in GitLab.
func (c *Client) CreateIssue(ctx context.Context, title, description string, labels []string) (*Issue, error) {
	body := map[string]interface{}{
		"title":       title,
		"description": description,
	}
	if len(labels) > 0 {
		body["labels"] = labels
	}

	urlStr := c.buildURL("/projects/"+c.projectPath()+"/issues", nil)
	respBody, _, err := c.doRequest(ctx, http.MethodPost, urlStr, body)
	if err != nil {
		return nil, fmt.Errorf("failed to create issue: %w", err)
	}

	var issue Issue
	if err := json.Unmarshal(respBody, &issue); err != nil {
		return nil, fmt.Errorf("failed to parse create response: %w", err)
	}

	return &issue, nil
}

// UpdateIssue updates an existing issue in GitLab.
func (c *Client) UpdateIssue(ctx context.Context, iid int, updates map[string]interface{}) (*Issue, error) {
	urlStr := c.buildURL("/projects/"+c.projectPath()+"/issues/"+strconv.Itoa(iid), nil)
	respBody, _, err := c.doRequest(ctx, http.MethodPut, urlStr, updates)
	if err != nil {
		return nil, fmt.Errorf("failed to update issue: %w", err)
	}

	var issue Issue
	if err := json.Unmarshal(respBody, &issue); err != nil {
		return nil, fmt.Errorf("failed to parse update response: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the raw response body (log respBody on parse failure) and the wrapped Unmarshal error
  2. Compare the failing field against the Issue struct and update the library or struct for your GitLab version
  3. Verify the endpoint is the real GitLab API (no interception)
  4. Pin library version to your server's GitLab version

Example fix

// before
issue, err := client.CreateIssue(ctx, title, desc, labels)
// after
issue, err := client.CreateIssue(ctx, title, desc, labels)
if err != nil {
	log.Printf("create failed (resp body may differ from Issue schema): %v", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

resp, _ := http.Post(baseURL+"/api/v4/projects/"+proj+"/issues", "application/json", bodyReader)
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
	return fmt.Errorf("unexpected content type %q; endpoint may be intercepted", ct)
}

Type guard

func isParseCreateErr(err error) bool {
	return strings.Contains(err.Error(), "failed to parse create response")
}

Try / catch

issue, err := client.CreateIssue(ctx, title, desc, labels)
if err != nil {
	if isParseCreateErr(err) {
		log.Printf("create succeeded at server? verify manually; response unparseable: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: POST /projects/:id/issues returns a non-issue JSON body — e.g. an error object (as JSON with 200 from a proxy), an HTML page, or GitLab schema drift making a field's type incompatible with the Issue struct.

Common situations: API gateways returning JSON error envelopes, GitLab upgrades changing field types, or pointing the client at a non-GitLab endpoint that echoes JSON.

Understand the failure class

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/8bf26e9db1ebf27c. Report an issue: GitHub.