---
id: troubleshooting
slug: /troubleshooting
title: 'Troubleshooting'
description: 'Diagnose Agility API problems by symptom — especially the ones that return a successful response while doing the wrong thing.'
sidebar_label: 'Troubleshooting'
sidebar_position: 9
tags: ['troubleshooting', 'errors', 'gotchas']
---

Start from what you are seeing, not from which API you are using.

The Agility API has a particular failure signature worth understanding up front: **many of its
traps return `200 OK`.** A mistyped parameter, an unencoded character or a missing flag will
frequently produce a well-formed, successful-looking response that is quietly wrong, rather
than an error you can catch. Most of this page is about those.

For more information, see [Error handling](/guides/error-handling).

## My Request Succeeded but the Data Is Wrong

The largest and most dangerous category.

| Symptom | Cause | Where to look |
| --- | --- | --- |
| History query returns current values, not values at that moment | `#` was not percent-encoded. Everything after an unencoded `#` is treated as a URL fragment and **never leaves your client** — the server answers a different question than you asked, successfully | [Attribute paths](/reference/query-grammar/attribute-paths) |
| `where`, `sort`, `page` or `find` appear to be ignored | On a single-asset `Data` URL such as `/Data/Story/1234` these are accepted and **silently dropped**. On `/Hist/...` they apply. `with` applies everywhere | [Query grammar](/reference/query-grammar) |
| Wrong page of results, or pages that overlap | `page=<size>,<start>` — **size comes first**, the opposite of most APIs. `page=0,50` is a zero-size page, not "the first 50" | [Sort and paging](/reference/query-grammar/sort-and-paging) |
| Paging repeats or skips assets between pages | Paging is applied after sorting, so an unsorted list has no stable order to page through | [Sort and paging](/reference/query-grammar/sort-and-paging) |
| `total` is missing from a paged response | A paged response only carries `total` when the server computed it. Whether it does depends on the per-instance `ApiDefaultNeedTotal` setting — send `needTotal=true` explicitly | [Sort and paging](/reference/query-grammar/sort-and-paging) |
| You get XML when you expected JSON | Omitting `Accept` gets you XML. `Accept` is also honoured as a **query-string parameter**, not only as a header | [HTTP headers](/apis/conventions/http-headers) |
| A relation update returned `200` but nothing changed | `act` was probably on the `Relation` element; it belongs on the `Asset` value element inside it. The misplaced form is accepted silently | [REST-1 writing data](/apis/rest-1#writing-data) |
| An operation returned `200` but the asset did not transition | Some redundant operations succeed and write a new moment without changing state — `200` means accepted, not transitioned | [REST-1 operations](/apis/rest-1#operations) |
| Response contains attributes you never selected | Selecting any *relation* attribute additionally returns up to three conditional attributes — commonly just its `Name`. This is implicit and cannot be turned off | [Query grammar](/reference/query-grammar) |
| Response is far larger than expected | Omitting `sel` does not return "nothing" — it returns a per-type basic attribute set. Combined with no default page size, an unbounded request can try to return your whole instance | [Query grammar](/reference/query-grammar) |
| Removing a tag removes nothing | The payload shape for remove is `removeFrom` plus `field`. A previously published example showed the *merge* shape instead | [Remove a tag](/apis/tags/remove) |
| History looks like it is missing versions | Moment numbers are instance-wide, not per-asset — they are not a contiguous per-asset sequence | [Moments and history](/concepts/moments-and-history) |
| Localized strings come back untranslated | An unmatched tag echoes back as a trimmed literal of the tag itself, so a typo is indistinguishable from an untranslated term | [Tag resolution](/apis/loc-2-v1/tag-resolution) |
| `?noloc` looks like it worked but changed nothing | `?noloc` does not work against Agility. It is a sample-application feature; appended to a `loc-2.v1` URL it is parsed as a *tag* and echoed straight back | [noloc](/apis/loc-2-v1/noloc) |

## I Got a Success Response but Nothing Happened

| Symptom | Cause | Where to look |
| --- | --- | --- |
| Bulk request returns `200` but assets are unchanged | A `200` means the request was processed, not that every command succeeded. Always check `commandFailures.count` | [Bulk errors](/apis/asset-bulk/errors) |
| A bulk command matches nothing, silently | `IsDeleted: true` is required to match deleted assets **even when `from` is an explicit OID token**. Without it the command matches nothing and reports no error | [Bulk queries](/apis/asset-bulk/queries) |
| Only one asset is affected when several matched | Subquery cardinality differs by position: in a relation update *every* match is used; in an operation's `args` only the **first** is | [Relation updates](/apis/asset-bulk/relation-updates) · [Operations](/apis/asset-bulk/operations) |
| Update fails with `ClosedAssetCannotEditExistingValues` | Closed assets accept only backfill: attributes with no value can be filled in, existing values cannot be changed. Reactivate the asset to edit it | [Operations](/concepts/operations#the-lifecycle-quartet) |
| Status update fails with `PrimaryWorkitemCannotHaveInvalidStatus` | The asset's team uses a Team Process, which restricts which `StoryStatus` values its primary workitems may enter. Use a status from the team's set (statuses whose `Team` relation points at that team) | [Workflows and status](/concepts/workflows) |
| Webhook created successfully but never authenticates | The `authorizationHeader` key was misspelled. Unknown keys are ignored, so the subscription is created, delivers with no `Authorization` header, the receiver 401s, that counts as a delivery failure, and the subscription is eventually auto-disabled. Tell-tale: `hasAuthorizationHeader` comes back `false` | [Create a subscription](/apis/webhooks/create) |
| Webhook update lost other fields | Update replaces the whole subscription; send the complete object, not just the changed field | [Update a subscription](/apis/webhooks/update) |
| Terminology change is not visible to clients | The `loc-2.v1` ETag can go stale and serve `304` with outdated text until the application restarts | [Hot reload](/apis/loc-2-v1/hot-reload) |

## My Request Fails

| Symptom | Cause | Where to look |
| --- | --- | --- |
| `Invalid SEL parameter`, `Invalid WHERE parameter`, `Invalid PAGE parameter` | Malformed query-string grammar. These are the literal wire strings — useful for diagnosis, but do not match on them in code | [Query grammar](/reference/query-grammar) · [Status codes](/reference/status-codes) |
| Querying a relation attribute fails on an abstract type | A downcast is required: `Workitems:Story.Category`, not `Workitems.Category`. Applies to `Workitem`, `BaseAsset` and `Scope` relations | [Attribute paths](/reference/query-grammar/attribute-paths) |
| `filter=` is rejected on `rest-1.v1` | There is **no `filter` query parameter** on `rest-1.v1` — `where` takes the full filter language. On `query.v1` the same two names mean different things | [Filter expressions](/reference/query-grammar/filter-expressions) |
| Activity stream request 400s on a valid-looking verb | Verb parsing is **case-sensitive**, and one wrong-cased verb fails the whole request. `direction` is case-insensitive | [Activity stream](/apis/activity-stream) |
| Bulk request returns a hard `500` | A missing request body produces a 500 rather than a 400 | [Request format](/apis/asset-bulk/request-format) |
| A large bulk request times out on the client | The server enforces no processing time limit and keeps running after your client gives up. Each command is its own transaction, so committed commands stay committed — there is no rollback or cancel. Re-query to find the actual state; never blindly resubmit the original payload | [Processing model](/apis/asset-bulk/request-format#processing-model-large-batches-run-to-completion) |
| Requests 404 after a URL change | Do not drop version suffixes — the `-2` in `loc-2.v1` is significant | [Base URL](/apis/conventions/base-url) |
| A malformed query string produces no error at all | Not every bad query string is rejected; some are accepted and ignored | [Status codes](/reference/status-codes) |

## It Behaves Differently on Another Instance

Behaviour that depends on deployment or configuration rather than on the API. Code that works
against one Agility instance can fail against another with no change on your side.

- **`needTotal` defaults per instance**, from the `ApiDefaultNeedTotal` setting — so a client
  that drives a pager off `total` can work in one place and break in another.
  See [Sort and paging](/reference/query-grammar/sort-and-paging).
- **`stackTrace` appears in error responses only on development builds**, which are
  internal to Digital.ai. Customer deployments — hosted and on-premise alike — run release
  builds, which never include stack traces or exception detail in API responses, and no
  setting can enable them. An example payload captured on an internal development instance
  may show the field; code must not expect it. See [Bulk errors](/apis/asset-bulk/errors).
- **Asset type and attribute names vary** with the methodology chosen at installation, and can
  be overridden by localization. Discover them from `meta.v1` rather than hardcoding them.
  See [Meta API](/apis/meta-v1) and [Terminology](/apis/conventions/terminology).
- **Custom fields are never in the default attribute set** and must be selected explicitly.

## Something Changed That I Did Not Expect

- **`POST api/webhook/delete` with no id deletes every subscription in the instance**, with no
  filtering by member. See [Delete a subscription](/apis/webhooks/delete).
- **Tag merge and tag remove have no undo.** See [Merge](/apis/tags/merge) and
  [Remove](/apis/tags/remove).
- **Bulk failures cascade.** A failure on a parent asset fails its dependents in the same
  request. Use [preview mode](/apis/asset-bulk/preview-mode) to validate before writing, and
  see [Bulk errors](/apis/asset-bulk/errors) for how to reissue only the failed subset.

## Still Stuck

- [Error handling guide](/guides/error-handling) — retries, backoff and failure classification
- [HTTP status codes](/reference/status-codes) — what each code means on this API
- [FAQ](/faq)
- [Sources and provenance](/reference/sources) — how far to trust any given page here, and
  which source wins when two disagree

If you hit something that is not here, it is worth writing down. Undocumented behaviour that
costs someone an afternoon is exactly what this page is for.
