Skip to content

pulse create: pulse is created, then the CLI fails parsing services/environments in the response #62

Description

@ded

Summary

rootly pulse create (v0.5.0) creates the pulse successfully, then exits non-zero because it can't parse the API's response:

Error: failed to create pulse: failed to parse response: json: cannot unmarshal array into Go struct field .data.attributes.services of type struct { Data []struct { Attributes struct { Name string "json:\"name\"" } "json:\"attributes\"" } "json:\"data\"" }

The pulse is recorded in Rootly (correct summary, source, services, environments, labels, refs). Only the CLI's post-create parsing fails, so the command reports failure for a request that succeeded.

Impact

rootlyhq/pulse-action (v2.0.0) builds its image from the latest rootly-cli release, so every GitHub Actions pulse now fails its step with the error above. Workflows that don't set continue-on-error go red on every deploy, even though the pulse arrived.

Reproduce

rootly pulse create \
  --services=<a service slug> \
  --environments=<an environment slug> \
  --labels="service=email,ref=main" \
  --source=github-actions \
  "Deployed email to staging"

The API returns 201. The CLI prints the error above and exits 1.

Cause

CreatePulseCLI in internal/api/client.go expects services and environments in JSON:API relationship form:

Services *struct {
    Data []struct {
        Attributes struct {
            Name string `json:"name"`
        } `json:"attributes"`
    } `json:"data"`
} `json:"services"`

But POST /v1/pulses (and GET /v1/pulses) returns them as flat arrays of objects inside data.attributes:

"services": [
  { "id": "…", "name": "Email", "slug": "email", … }
],
"environments": [
  { "id": "…", "name": "Staging", "slug": "staging", … }
]

json.Unmarshal of an array into that struct fails, which aborts the whole parse. The same mismatch applies to environments.

Suggested fix

Model both fields as flat arrays, matching the API:

Services []struct {
    Name string `json:"name"`
} `json:"services"`
Environments []struct {
    Name string `json:"name"`
} `json:"environments"`

Then map s.Name / e.Name into pulse.Services / pulse.Environments. Alternatively, treat a parse failure after a 2xx as a warning, since the pulse was created. A test in client_pulse_test.go with a real POST /v1/pulses response body that includes services and environments would catch this.

Environment

  • rootly-cli v0.5.0 (latest, pulled by rootlyhq/pulse-action@22355de7941ef67a661e0873006ec17b33224fb5, v2.0.0)
  • GitHub Actions, ubuntu-latest
  • Observed 2026-09-24

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions