Summary
WorkspaceUpdateOptions.VCSRepo is tagged jsonapi:"attr,vcs-repo" without omitempty. This means any call to Workspaces.Update() that doesn't explicitly set VCSRepo sends "vcs-repo": null in the request payload, which Scalr interprets as "delete the VCS repository configuration." This silently strips VCS settings (trigger patterns, branch, working directory) from the workspace.
The same issue affects Terragrunt in the same struct.
Reproduction
// Intending to only change execution mode:
client.Workspaces.Update(ctx, wsID, scalr.WorkspaceUpdateOptions{
ExecutionMode: scalr.WorkspaceExecutionModePtr(scalr.WorkspaceExecutionModeLocal),
})
// Result: execution mode changes, but VCS repo is also deleted.
Expected behavior
Setting only ExecutionMode should not affect VCSRepo. The Scalr API supports partial updates — absent fields are treated as "don't change." The scalr CLI correctly implements this by only including fields the user explicitly passed.
Root cause
The svanharmelen/jsonapi serializer uses omitempty as its sole mechanism to distinguish "don't send this field" from "send null." Every other pointer field in WorkspaceUpdateOptions has omitempty and works correctly for partial updates. VCSRepo and Terragrunt omit it so that nil can express "delete this resource," but this makes it impossible to leave those fields untouched.
// These fields work correctly for partial updates (omitempty present):
Operations *bool `jsonapi:"attr,operations,omitempty"`
ExecutionMode *WorkspaceExecutionMode `jsonapi:"attr,execution-mode,omitempty"`
// These fields are destructive on every update (omitempty absent):
VCSRepo *WorkspaceVCSRepoOptions `jsonapi:"attr,vcs-repo"`
Terragrunt *WorkspaceTerragruntOptions `jsonapi:"attr,terragrunt"`
Possible fixes
- Tri-state wrapper type that distinguishes Unset / Null / Value, allowing
omitempty to be restored while still supporting explicit deletion.
- Separate boolean fields like
ClearVCSRepo *bool for the delete operation, with omitempty added back to VCSRepo.
- Custom
MarshalJSONAPI implementation on WorkspaceUpdateOptions that skips nil fields regardless of tag.
Workaround
We work around this by sending a raw HTTP PATCH with a hand-built JSON:API payload containing only the field we want to change, bypassing the SDK entirely.
Affected version
v0.0.0-20251128082633-2ff36e0bea2a
Summary
WorkspaceUpdateOptions.VCSRepois taggedjsonapi:"attr,vcs-repo"withoutomitempty. This means any call toWorkspaces.Update()that doesn't explicitly setVCSReposends"vcs-repo": nullin the request payload, which Scalr interprets as "delete the VCS repository configuration." This silently strips VCS settings (trigger patterns, branch, working directory) from the workspace.The same issue affects
Terragruntin the same struct.Reproduction
Expected behavior
Setting only
ExecutionModeshould not affectVCSRepo. The Scalr API supports partial updates — absent fields are treated as "don't change." ThescalrCLI correctly implements this by only including fields the user explicitly passed.Root cause
The
svanharmelen/jsonapiserializer usesomitemptyas its sole mechanism to distinguish "don't send this field" from "send null." Every other pointer field inWorkspaceUpdateOptionshasomitemptyand works correctly for partial updates.VCSRepoandTerragruntomit it so thatnilcan express "delete this resource," but this makes it impossible to leave those fields untouched.Possible fixes
omitemptyto be restored while still supporting explicit deletion.ClearVCSRepo *boolfor the delete operation, withomitemptyadded back toVCSRepo.MarshalJSONAPIimplementation onWorkspaceUpdateOptionsthat skips nil fields regardless of tag.Workaround
We work around this by sending a raw HTTP PATCH with a hand-built JSON:API payload containing only the field we want to change, bypassing the SDK entirely.
Affected version
v0.0.0-20251128082633-2ff36e0bea2a