Developer-surface failures cluster into a few families — identity, workspace resolution, permission, naming, and drift — and each has a distinctive fingerprint. Work top-down: xmatix doctor first for anything CLI-shaped (it tests workspace, config, token and API round-trip in order and names the first failure), then the specific symptom below.
Sign-in and token problems
"No cached account; run xmatix login" — or doctor's token check fails. The tenant's token cache is empty or unusable on this machine: first use, a cleared cache, or an expired session that can no longer refresh silently (password change, revoked session, long idle). Run xmatix login; the persisted config supplies everything except your credentials. On CI runners this is the expected failure mode after provisioning a new runner — the device-code sign-in must be completed once on that machine, because non-interactive sign-in is not available yet (Authentication).
Login succeeds but API calls return 401. The token and the target disagree. Check the workspace config as a set: apiBaseUrl and scopes must belong together — when scopes is empty the CLI derives it from apiBaseUrl, so an edited base URL silently changes the audience the token is requested for. After changing apiBaseUrl or authority, xmatix logout then login to mint tokens for the new values.
whoami shows the wrong user or tenant. You are in a different workspace than you think — the CLI resolves everything from the nearest .xmatix/ up the directory tree. xmatix doctor prints the resolved root; multi-tenant setups should keep one workspace per directory precisely so that "where am I?" has one answer.
Workspace and version problems
"No xMatix workspace found. Run xmatix init in your repository root first." No directory from the cwd upward contains .xmatix/. Either you are outside the repository, or the repo was cloned without the workspace (someone gitignored .xmatix/ wholesale instead of only its cache/ and tokens/). Fix the checkout or init + login fresh; the artifact tree still round-trips.
Doctor flags authority / clientId / scopes as missing. .xmatix/config.json exists but was never fully populated — typically a hand-created file or a partial first login. One complete xmatix login --tenant … --api-base-url … --authority … --client-id … persists the full set; doctor should then pass end to end.
Commands behave differently on two machines. Compare xmatix --version — a stale global tool is the usual cause, and dotnet tool update -g xmatix.cli the fix. Pin the version in CI images so pipelines and laptops agree on file formats and verbs.
Push rejections
Before debugging a script's behavior, read its deployment state in the product: open the script under Setup → Process Studio → Scripts and look at the statistics bar (Status, Active Version, Versions, Bindings) and the Versions tab. Every push stores a version row with its own compile status; a newer row is not proof that runtime behavior changed — only the row tagged Active handles work, and the lightning action on a row is how a stored version is activated by hand.
- 1
Active Version in the statistics bar says whether any version is live; Versions and Bindings count what a push and its bindings left behind.
- 2
The Versions tab is the deployment evidence — switch here before reading the source on the Script tab.
- 3
Status is the compile result of that stored push; a version that failed to compile is kept for diagnosis but can never be activated.
- 4
The Active tag marks the one version the runtime executes — a newer Compiled row above it changes nothing until activated.
- 5
Row actions: load the version into the editor, activate it (the lightning icon — what --activate does from the CLI), or delete it.
scripts push fails with compile diagnostics. The server stores the attempted version, records compiler diagnostics against it and leaves the previously active version unchanged; the CLI exits with code 3. Read the line-referenced diagnostics, fix the local .csx, and push again. The failed version reached version history for audit, but it did not become the runtime version.
scripts push stops with a hash mismatch. The script changed on the server since your last pull — someone edited it in the product. Pull, reconcile in git (the diff shows the in-product change), then push; use --force only once you have decided the local file should win. Treat this as a merge conflict, not an obstacle.
A push "succeeded" but behavior didn't change. For scripts, the version uploaded without --activate — the previous version is still the active one; on the Versions tab the new row shows Compiled but the Active tag stays on the older row, and the header's Active Version is unchanged. For workflows, the definition updated as a draft — triggers only register from the published version, so publish it or push with --publish (Workflows).
metadata push seemed to skip rows. Only tenant-owned rows are applied on import; base and package-owned rows in the file are ignored by design. Export with the default TenantOwned scope for anything you intend to round-trip (Metadata).
Data API symptoms
401 on every call. The bearer token is missing, expired, or minted for a different audience than the API host you are calling. Decode nothing — just re-acquire a token for the right base URL and retry; persistent 401s with a fresh token mean the client registration or authority is wrong.
403 or records mysteriously missing. The API enforces the caller's permissions — security profiles, record security, restriction rules — identically to the UI. A record the integration "can't see" is a record its user can't see; sign in to the product as that user and look. Fix the account's access, not the query (security model).
404 on an entity route. The path takes the entity's logical name, not its display label — Account, not "Accounts". Check the name in Setup → Design Studio → Entities, via metadata pull, or with the MCP endpoint's describe_entity tool (MCP).
An update is rejected with 409 saying the record is being edited. Another user holds the record's edit lock in the product; the response names the holder. This is the platform's concurrent-edit protection, applied to API writers too; retry after the lock clears rather than working around it.
An action call returns success but nothing happened. A named entity action with no bound handler succeeds without effect by design. Verify the action has a bound automation script or built-in behavior, and that the name in your route matches the action's name exactly — the dispatch is keyed on the name.
Drift symptoms
The drift gate fails but nobody changed anything. Someone did — in the product. migrate plan --keep-server-snapshot retains the live export under xmatix/.snapshot/server/, so you can diff the actual files and identify the change. The remedy is a fresh snapshot export committed through review; forcing an apply over unexported changes is how in-product work gets destroyed.
Pull created duplicate-looking files. An artifact was renamed in the product: pulls write files by logical name, so the new name arrives as a new file and the old file goes stale. Delete the stale file in the same commit — otherwise a later import pushes the old artifact back (Workspace layout).
