On this page

Runbook

Start from the first error line. Operational failures exit 1 and preserve underlying causes below Caused by:. Invalid command-line arguments exit 2 with clap's own message; a required setting left out is one of them. A style or language the catalog does not hold is an operational refusal: exit 1, with the catalog's location and the names it does hold.

Fleet database unreachable

Every command that keeps or reads state goes through Skryba's fleet database. skryba database check walks the same steps and names the one that failed:

stado database resolve skryba --consumer skryba --json failed … — stado is not on PATH, or the machine's Stado does not declare skryba for consumer skryba. Run the same stado command to see its own sentence.

stado service directory connect skarbiec --consumer skryba --json failed … — this machine has no Skarbiec route for skryba; the route is declared with stado service directory consumer-add skarbiec skryba --target <HOST> --bind <IP:PORT>.

reading the skryba-database-client bearer <PATH> — the bearer file is missing; it is delivered with stado credentials token sync skryba-database-client.

Skarbiec refused skryba-database-client reading skryba-database#<FIELD>: HTTP 403 … — the grant does not cover pooler_url or ca_certificate; widen it on the vault owner with stado credentials grant item-read skryba-database-client skryba-database --field <FIELD>.

connecting to the skryba database pooler (stado://database/skryba) — the cause below names TLS (invalid peer certificate: the item's ca_certificate does not sign the pooler) or the network.

SKRYBA_DATABASE_SCHEMA=… is not a schema name — use lowercase letters, digits and underscores, starting with a letter.

No detector can run

no detector credentials found; configure GPTZERO_API_KEY, WINSTON_API_KEY, ORIGINALITY_API_KEY, or SAPLING_API_KEY

Set at least one non-empty authorized key, or in Desktop put it in ~/.config/skryba/env. If a specific list is required, put global --detectors before the subcommand. Style and language choices do not bypass this requirement. This refusal occurs before generation or judging; it is not a failed model quality assessment.

--detectors must contain at least one detector name — remove an empty explicit list or supply names.

unknown detector "…"; available detectors: gptzero, originality, sapling, winston — correct the name.

<KEY> is required for <name> — an explicitly selected detector lacks its key.

Detector request or response failed

<name> request to <endpoint> failed — network, DNS or TLS failure on the one request a score sends. Inspect the cause chain and configured *_API_URL.

<name> returned <status>: <body> — the provider rejected the request, throttled it, or was unavailable; the body is its whole answer. Skryba sends each score once and does not retry; if the provider throttles, state a pause with global --detector-interval-ms.

<name> returned invalid JSON or response has no recognized probability in [0, 1] — provider response shape changed. Preserve the response body and selected endpoint when reporting.

<name> cannot score empty text — provide non-whitespace input.

Score cache failed

creating the detector score cache in stado://database/skryba/<SCHEMA>, reading a cached <DETECTOR> score from …, caching a <DETECTOR> score in … — the database answered with an error; the cause chain carries Postgres's own sentence. See Fleet database unreachable when it is the connection.

Do not delete rows from scores casually: subsequent identical texts will be billed again.

Model inference failed

--brama-url (or BRAMA_URL) requires BRAMA_TOKEN_ROLE naming the client token as ROLE#FIELD — configure the Brama client-token role reference. To run without Brama, explicitly select --model-service-url and an owner-only bearer file named by SKRYBA_MODEL_SERVICE_KEY_FILE; a direct service is never a retry after Brama fails.

--model-service-url (or SKRYBA_MODEL_SERVICE_URL) requires SKRYBA_MODEL_SERVICE_KEY_FILE naming an owner-only bearer file — name a regular owner-owned key file holding the service's bearer.

--brama-url (or BRAMA_URL) cannot use SKRYBA_MODEL_SERVICE_KEY_FILE or --model-service-url (or SKRYBA_MODEL_SERVICE_URL) cannot use Brama token or signing declarations — remove the crossed declaration or select its matching URL. No credential is sent to a route of the other kind.

<VARIABLE> must be a role reference ROLE#FIELD, not … — supply both the role and field. Vault item names are not this contract.

<VARIABLE>: <BINARY> credentials get --role <ROLE> --field <FIELD> failed: … — the credential resolver refused; the retained stderr names its cause. The CLI does not silently switch credential sources. On a machine without Stado, explicitly select SKRYBA_CREDENTIALS_FILE as described in credential boundary.

SKRYBA_AGENT_ID and SKRYBA_AGENT_AUTH_SECRET_ROLE must be configured together — configure both agent identity and role reference, or neither.

model request to <URL> failed — inspect reachability, TLS and the chosen BRAMA_URL or SKRYBA_MODEL_SERVICE_URL. The wait line on stderr names the endpoint and when the request started; its failure line names the transport cause.

model endpoint <URL> returned <STATUS>: <BODY> — authentication, routing, model or service failure from the selected endpoint.

reading model response from <URL> with status <STATUS> — headers arrived but body reading failed; the cause retains the transport error and the observed status.

cannot sign Brama request: system clock precedes the Unix epoch — the role route cannot supply a valid agent signing timestamp. The owner-only key-file route sends no signature.

invalid model response JSON, model response has no string choices[0].finish_reason, model response has no string choices[0].message.content, model endpoint returned empty message content, or model response was truncated at the model's own output limit — the completion is unusable. The parse refusal names the endpoint and preserves the response; a model-imposed truncation is not a Skryba token budget.

Rewrite or judge refused locally

  • --minimum-quality is required: the quality score a rewrite must reach is the caller's to state
  • input text cannot be empty
  • candidate count must be positive
  • maximum words per chunk must be positive
  • temperature must be a finite number of at least 0 (Skryba keeps no ceiling; Brama refuses one above its own bound and says so)
  • minimum quality must be between 0 and 1

Correct or state the named value. Desktop names an empty setting before it runs and sends the values you typed.

quality judge did not return a valid evaluation — the judge's reply failed JSON-boundary, schema, or [0,1] score validation; the nested cause says which. The judge is asked once per candidate. style_matched and language_matched are required booleans; missing values are schema errors, not passing defaults.

Requested style or language refused

style "<NAME>" is not in stado://database/skryba/<SCHEMA>; available styles: <NAMES>; add it with `skryba style add <NAME> --instruction <TEXT>` — the catalog in that schema has no such style. Use one of the listed names, or add it. If the list is not what you expected, the run read a different schema: check SKRYBA_DATABASE_SCHEMA. The same sentence with language covers --language.

style name "<NAME>" is not valid: ..., ... needs an instruction ..., ... already exists in ..., ... cannot be removed ... — skryba style and skryba language refused a change; each sentence says what to do, and the catalog page lists them. The rows are unchanged.

creating the catalog in … / writing the built-in styles and languages to … — the database refused the catalog's tables or seeds; the cause is Postgres's own sentence.

chunk <N>: no candidate passed every check for the requested style '<STYLE>' and language '<LANGUAGE>'; source fallback refused ... — no generated candidate passed every check, and the original does not satisfy one or both profile requirements. The diagnostic gives the source's match booleans, the number of evaluated candidates, style and language rejection counts, all-quality rejection count, and source judge issues. Counts can overlap. A candidate may match both profile choices yet fail meaning preservation, grammar, or another quality check; this refusal does not say every candidate had the wrong style or language. Use a generator and judge that support the language and can preserve the source's detail in the requested register, or make the entry's instruction clearer with skryba style edit. Do not treat an unchanged original in the wrong language as a translation.

This refusal leaves final output and report files unwritten, including existing files at those destinations. Previous calls and cache writes remain. If the original does match the profile, it may be retained with used_source_fallback: true, even if its numeric quality thresholds did not pass. The report makes both facts visible.

unrecognized subcommand 'style', unrecognized subcommand 'database' or unexpected argument '--profile-store' found — the resolved CLI and the caller disagree about the version. Update the executable together with Skryba Desktop; installing another copy later on PATH does not update the one the app uses.

Policy or training failed

reading policy <path> / parsing policy <path> — file missing, unreadable, or invalid JSON.

unsupported policy version …, policy vectors do not match the strategy count, policy contains a non-finite value — the policy is incompatible or corrupt; use a known version-1 file.

training requires at least one epoch and two generations per group — use --epochs >= 1 and --generations >= 2.

training reward weights and penalty are invalid — weights and penalty must be non-negative and detector plus quality weight must be positive.

unsupported training data extension "…"; use .json, .jsonl, or .txt — change the format or extension.

JSON training data must be an array, training row must be a string or object, training row has no non-empty text field, training data contains no text examples — correct the shape documented in train.

writing policy … / publishing policy … — make the output parent writable; check for a sibling .json.tmp left before rename.

Writing style refused

writing-style add needs at least one file or directory — name at least one path. From the CLI, clap refuses first with the following required arguments were not provided: <SOURCES>....

writing-style ingestion needs 0 < --min-words <= --max-words — correct the bounds; both are required and Skryba assumes neither.

writing-style source <PATH> is neither a file nor a directory — the path does not exist, or it is a socket or device. Check the spelling and the expansion of ~.

neither HOME nor the user database names a home directory, so `~` cannot be expanded — a CLI path began with ~, but the process has no home to expand it against. Set a valid home in the launch environment or pass an explicit absolute path; the command refuses before reading or writing a literal ~ directory. Desktop surfaces the same CLI error.

no prose of <MIN>..<MAX> words in the named sources — the sources held no passage long enough. Lower --min-words or name files with authored prose: code blocks, tables, images, HTML and bare links are dropped. A directory walk asks the selected model route which UTF-8 files hold prose rather than filtering names by extension.

reading <PATH> / listing <PATH> / reading the kind of <PATH> — a file could not be read, a folder could not be listed, or an entry's kind could not be read; the cause chain names the filesystem error.

writing-style keep needs both --machine and --kept, or neither and a JSON pair — pass both files, or neither and let --pair read the JSON object (default -, stdin).

parsing the JSON pair; expected {"machine": …, "kept": …} — the pair is not that object; the serde cause names the line and column.

a preference needs both a kept text and a machine text — one side was empty or whitespace after trimming.

the kept text is identical to the machine text; that is a rewrite nobody changed, not a preference — edit the kept text, or record nothing. A preference with equal sides teaches nothing.

the writing-style store holds no passages; run `skryba writing-style add` first** and **the writing-style store holds no preferences; run `skryba writing-style keep` first — the schema holds no rows for that format. Check SKRYBA_DATABASE_SCHEMA: an unexpected empty store usually means a different schema than the one that was filled.

--lead-words is required for sft: how many words of a passage's opening become the prompt is the caller's to state — state --lead-words for an SFT export or training run.

writing-style export needs a positive --lead-words — 0 leaves no prompt.

every held passage was too short to split into a <N>-word lead and a completion — --lead-words exceeds what the held passages can give twice over. A passage is exported only when the lead and the remaining completion each carry at least --lead-words words.

creating the writing-style tables in …, adding a passage from <SOURCE> to …, keeping a preference in … — the database refused the write; the cause is Postgres's own sentence.

A duplicate is not a failure: re-adding held prose or re-keeping a recorded pair counts in skipped and exits 0.

Training or local generation refused

ster is not installed: install it with `cargo install --git https://github.com/wisent-ai/ster --locked`, or put the `ster` binary on PATH. — Skryba looks in every PATH entry, then ~/.cargo/bin, ~/.local/bin, ~/.stado/bin, /opt/homebrew/bin, and /usr/local/bin. writing-style train says this before it writes the export, so nothing is left behind.

`<the ster command>` exited with status <N> / `<the ster command>` exited with a signal — Ster refused or died. Ster has already printed why on the inherited stderr; read that line, not this one. The named command is runnable as-is, which is the fastest way to reproduce it.

`<the ster command>` reported success but wrote no adapter at <PATH> — ster tune exited 0 and nothing is at --output. Check that the path's parent exists and is writable, then re-run the printed command by hand.

--generator local needs --local-model naming the checkpoint Ster maps and the local generator needs a checkpoint; pass --local-model — the local rewrite route needs the checkpoint the adapter was trained against.

--generator local needs --local-max-new-tokens; Ster assumes no token budget — state how many tokens each local candidate may generate.

--local-max-new-tokens must be positive — 0 generates nothing; state a positive budget.

--method dpo needs --beta; Ster assumes no preference strength — writing-style train --method dpo needs --beta; it is refused before the export is written. --beta belongs to --method dpo; sft has no reference to pull toward refuses the reverse.

candidate <N> (<strategy>) came back as whitespace from `ster generate`; dropping it from the group — not an error, and not on stdout. Ster exited 0 and wrote only whitespace, so that candidate is dropped and the run continues on the rest. If none passes, the original is retained only when it meets the requested style and language; otherwise source fallback is refused. If every candidate is empty, check that the checkpoint answers the selected chat template and that the generation budget is suitable. A stand-in judge cannot validate the profile or meaning preservation.

Ster's own refusals reach you unchanged. Two seen in practice: this Ster binary was built without the metal feature — the installed ster has no --device metal, so use cpu or install a Metal build — and failed to read model repository <NAME>, whose nested cause is the Hugging Face status code, 401 for a name that does not exist or a private repository with no token in $HF_HOME/token.

Corpus and benchmark

curated corpus <path> contains no samples, curated sample <id> carries no text, or declared word/SHA mismatch — regenerate declarations from exact UTF-8 input; do not edit only the checksum to hide an accidental content change.

benchmark needs --corpus or at least one documentation root — supply one mode.

benchmark accepts either --corpus or documentation roots, never both — choose one.

benchmark sample and candidate counts must be positive, benchmark word bounds are invalid, or no qualifying passages found — correct numeric bounds or provide Markdown files with qualifying prose.

Harvest found nothing, or could not read a source

opening the Messages database <PATH> or opening the Notes database <PATH> — the path is wrong, or the process running Skryba (your terminal, or the Stado worker for a scheduled run) has no Full Disk Access to the Messages or Notes container. Grant it in System Settings › Privacy & Security › Full Disk Access and run skryba writing-style harvest run <NAME> again.

harvest run: <F> of <N> harvesters failed; the others added <A> passages … — each failed harvester is named on its own line with its cause; the rest already added their writing. Fix the named one and run it alone.

A run that reads messages but adds none — every run of your own messages was shorter than the harvester's --min-words. Lower it with skryba writing-style harvest edit <NAME> --min-words <N> --reset and run again.

transcript-lake is not installed … — install it with stado product install transcript-lake, or put the binary on PATH.

Stado does not know host <HOST> … — schedule set --host takes a Stado registry host; the refusal below it lists the hosts Stado holds.

Samples cannot be planned or written

the writing-style store holds no passages; a plan divides the pairs by the writing you hold … — add or harvest your writing first; a plan is proportional to it.

unfilled above zero after samples generate — a kind and length of your writing has fewer unused passages than samples to write. Harvest more of it, or plan fewer pairs or fewer length bands.

BRAMA_TOKEN_ROLE is required for Brama inference … or Brama returned <STATUS>: … — samples generate reaches the model the way rewrite does; the same fixes apply. A plan with nothing left to write needs no credential.

A writing rule refuses or rejects every candidate

rule <NAME> counts a phrase: pass --phrase …, --phrase belongs to --metric phrase …, rule <NAME> needs a limit … — see skryba rule for every refusal.

A rewrite that keeps the source text, with used_source_fallback: true and every candidate's rules_passed: false — every candidate broke a held rule, so the chunk kept its source. Read each candidate's rule_violations in the --json report, then loosen the rule with skryba rule edit or set it from your own writing-style profile. When the source itself fails the requested style or language, the chunk is refused instead, and the refusal counts the candidates rejected for writing rules.

Lyrics refused, or no draft met the form

lyric form <NAME> is not in <STORE>; held forms: … — the form named by --form was never added in this schema; add it with the command the refusal prints, or pick one skryba lyrics form list shows.

scheme <SCHEME> of lyric form <NAME> holds …, … gives <LETTER> to one line only …, … asks for at least <MIN> and at most <MAX> syllables …, refused ending <ENDING> … is not valid … — the form itself is malformed; see skryba lyrics for every refusal.

check prints passed: false — read violations: each sentence quotes the line and the count it broke. A line counted with fewer syllables than you hear holds a loanword spelled as written or a digit; spell it as it is said.

no lyrics met form <FORM>: … — every draft compose measured still broke a count; the refusal quotes the closest draft's. Raise --candidates or --revisions, loosen the form (skryba lyrics form edit), or drop a refused ending, and keep --report to read every draft. The model route's own failures read as under Brama inference failed.

Maintained as part of the website-owned Skryba documentation corpus.