Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Troubleshooting

A short list of things that go wrong and how to fix them. If none of these match, check the request id in the response envelope and grep the ScaiGrid logs.

Publish returns SCAISKILLS_VALIDATION_FAILED#

The validator's context.errors array names the exact stage that failed. Common ones:

  • Slug mismatch. The name field in SKILL.md's frontmatter must equal the URL slug. A common copy-paste error after renaming.
  • Non-monotonic version. The version in the manifest must be strictly greater than every previously published version for this skill. Republishing 0.1.0 after 0.2.0 is rejected; bump to 0.2.1.
  • Missing SKILL.md. The bundle has to contain SKILL.md at the tar root (or under a single top-level directory). Bundling from inside the directory (tar -czf x.tar.gz *) usually works; bundling the directory itself can put the file one level too deep.
  • Frontmatter not parsed. Make sure the file starts with --- on its own line and the closing --- is also on its own line.
  • References path escape. references/../etc/passwd or absolute paths in the references directory are rejected.

Publish returns SCAISKILLS_VERSION_CONFLICT#

The semver you uploaded already has a published row for this skill. Even if the bundle bytes are identical, the version row is unique on (skill_id, semver). Bump the semver in SKILL.md and re-tar.

Binding stays in pending_grants after a grant call#

The grant flow only clears the permissions axis. If pending_grants won't flip even after granting every declared permission, the binding still has unmapped required secrets. Required secrets are supplied at bind time only — there's no endpoint to add a missed one. Delete the binding and recreate it with complete secret_mappings.

Less commonly: a permission string was mistyped on the grant call. The match is byte-equal against the manifest's permissions: list. Compare them side by side.

Resolve returns an empty skills list#

In rough order of frequency:

  • No bindings for the scope. GET /bindings?scope_type=workspace&scope_id=... should show at least one row.
  • All bindings are pending_grants: true. They're filtered from resolve. Clear the gates.
  • All bindings are enabled: false. They're filtered too — check the enabled flag on each row.
  • Wrong scope id. ScaiWave channel ids and ScaiGrid room ids are not the same string in every deployment; confirm what the runtime is actually sending.

Binding to @latest resolves to an old version#

The resolver picks the highest published, non-yanked version at bind time. If a newer version was published after the bind, the binding won't see it — floating refs do not re-resolve. Delete and re-create the binding to roll forward, or bump to a pinned version.

If you genuinely expected a newer version to exist, double-check it isn't sitting in status: "draft" or was yanked.

skills.view returns SCAISKILLS_SKILL_NOT_FOUND for a slug that exists#

view enforces scope gating: the slug must appear in the caller's resolved set. If the caller's scope has no active binding for the skill, view returns 404 even when the skill itself is published. Check the resolve output for the same scope first.

Yanked version still showing up in latest resolution#

Yanked versions are skipped by latest and floating refs on subsequent binds. Existing bindings that resolved to the yanked version before the yank keep working — yank is non-disruptive by design. If you need them gone, delete and re-bind.

Bundle upload returns 413 or hangs#

The bundle is too large or the upstream is dropping the request. Check the SCAISKILLS_BUNDLE_TOO_LARGE cap in your deployment's settings. If you're below the cap, the file is genuinely too large for the path — split the references into separate skills, or store reference content in ScaiDrive and read it from inside SKILL.md instead of bundling it.

Search returns nothing or wrong results#

skills.search runs through ScaiMatrix first; on failure it falls back to a substring filter over the resolved set. Empty results usually mean:

  • ScaiMatrix not configured. settings.weaviate_url empty → no semantic index, substring fallback only. Set the URL or accept the fallback.
  • Out-of-scope match. The semantic search returned matches but they're not in the caller's resolved scope, so they're stripped. Confirm the binding for the expected slug exists in this scope.
  • Index lag. Indexing runs best-effort at publish time. A freshly published skill may not be in the index for a few seconds. The substring fallback works in the meantime.

Cache feels stale after editing a binding#

Every write to the binding table invalidates the relevant scope's cache key. If you're still seeing the old answer after a known-good write, three usual causes:

  • Wrong scope key. You wrote to (channel, chn_xyz) but the runtime is resolving against (workspace, ten_xyz). Resolution merges scopes but caching is per-primary-scope. Invalidate by issuing a write against the primary scope, or wait 60 seconds.
  • Redis unavailable. Cache writes silently fail; reads silently miss. The next resolve hits MariaDB directly — usually self-healing.
  • Long-lived runtime client. If the runtime caches results locally on top of ScaiGrid's cache, the runtime's local TTL dominates. The 60 s ScaiGrid TTL is the floor, not the ceiling.

ScaiCore picks up the wrong skills at boot#

ScaiCore resolves at start with scope_type: core, and the resolved set is pinned for the Core's lifetime. If the Core is showing stale skills:

  • Check whether the Core is being restarted at all on config change — pinning is by design, so restarts are how rollouts happen.
  • For required skills declared in the Core's metadata, a missing or yanked binding fails the start outright. A required: false skill missing just warns and continues.

Dependency resolution fails with SCAISKILLS_UNRESOLVABLE_DEPENDENCY#

A requires.skills ref in the bundle didn't match any published, non-yanked version. Most often this is a slug typo in the depending skill's manifest, or the dependency skill itself was yanked across every version that satisfies the range. Check the depending bundle's manifest.requires.skills against GET /skills/{slug} for the dependency — the published versions array should show at least one match for the range.

A bind succeeds but resolved_deps_json is empty#

That's not necessarily wrong — empty means the skill declared no requires.skills. The lockfile only contains transitive dependencies, not the root skill itself (the root is identified by the binding row's own skill_id and resolved_version). To confirm the dependency list was actually empty, look at the skill version's manifest in the catalog.

Updated 2026-05-18 15:01:32 View source (.md) rev 12