Troubleshooting
Each entry starts with what the organism prints, word for word, with … where a name, a number or a digest goes. Then the cause, then what to do. The entries…
Each entry starts with what the organism prints, word for word, with
… where a name, a number or a digest goes. Then the cause, then what
to do. The entries are grouped by the part that owns the failure.
A verb that fails prints hale dna: and the reason. A running body
prints its lines as hale dna run: or hale dna dev: , and a node as
hale node <name>: .
Four verbs answer with nothing running, and most questions start with one of them:
hale dna status # the projection: organism, journal, reviews, body, memoryhale dna board # what waits on the Board, secrets missing, the latest violationshale dna history t1 # every row about one entity, by its causal linkshale dna secrets # every secret the organism needs, and whether the vault holds itThe record
Section titled “The record”Nothing is running here
Section titled “Nothing is running here”organism: not running — reading the Journalstatus says this when no body in this clone holds the record’s
lease. It is not an error: status, history, board, review <id>
and ui read the record. Asking for work needs an organism to answer.
With a remote, the ask goes into the record and a body elsewhere
answers it. Without one, start a body here with hale dna run (or
hale dna dev) in another terminal.
the organism is not running here (no body in this clone holds the record's lease) and the repository has no remote to reach one through; start it with `hale dna run`Same cause, and nowhere to send the ask. Start a body, or add a remote with a body behind it.
requested intent …, but the organism journaled no answer in time (record revision …)hale dna task create wrote the ask and no admission or refusal came
back in time. The ask stays in the record. Check that a body is
running (hale dna body), and look at its terminal for a no nerves:
line (The nerves): a body that hears no nerves relays
nothing.
No record, or a toolchain that moved
Section titled “No record, or a toolchain that moved”no record at refs/dna/journal in … (run `hale dna init`)The directory has DNA files but no record. Run hale dna init in it.
embedded dna: … (hale …); vendor/dna was materialized from … — run `hale dna upgrade`The first line of status. The toolchain you are running embeds a
different DNA source from the one vendor/dna was copied from. Run
hale dna upgrade, which copies this toolchain’s core into
vendor/dna again.
The chain does not verify
Section titled “The chain does not verify”journal: … event(s), chain BROKEN at …refs/dna/journal is one commit per row. At the head status loaded,
the number of commits differs from the number of rows: the ref’s
history was rewritten. git reflog refs/dna/journal lists the heads
the ref had before; a clone that still holds the old head has the
rows. See Memory and the record.
A fetch loses a ref lock
Section titled “A fetch loses a ref lock”fetch origin: … cannot lock ref …From sync, ledger adopt or a body. Another git process held a ref
the fetch was updating. The record tries such a fetch five times
before it gives up, and nothing changed. Run the command again. If it
keeps failing, look for a stuck git in that clone, or a stale
.lock file under .git/refs/dna/ left by one that was killed.
A sync refuses to re-sign someone else’s row
Section titled “A sync refuses to re-sign someone else’s row”the record diverged, and local event … (… …, by …) was signed with …, not this clone's; a reconcile would re-sign it as this clone's, so the record was not changed and every local row is kept at refs/dna/journal. Have that row's writer sync first — a writer rebuilds its own rows — then sync again to fast-forward; or fetch and fast-forward once the remote holds itUnder dna.trust = signed, hale dna sync would have to rebuild a row
this clone did not sign. It refuses before any ref moves; nothing is
lost. Do what the message says.
A connection that cannot be made
Section titled “A connection that cannot be made”connect: … could not be read (…); nothing was changedconnect: no record identity at … (`refs/dna/identity`; a record publishes it when it syncs — `hale dna sync` there)The first: the URL is wrong or unreachable from here. The second: the
other record exists but has never synced, so it has not published its
identity. Run hale dna sync in the other record, then connect again.
Memory
Section titled “Memory”Working memory is Postgres. Memory and the record covers the ledger, the roles and protected evidence.
The ledger does not answer
Section titled “The ledger does not answer”memory: record + ledger (routing 1, adopted at …; operations and leases live in memory; THE LEDGER IS UNREACHABLE (…): what is read here is the last projection, and nothing is admitted until it answers)The memory: line of status, on an organism that adopted the
ledger. What you read is the last projection this clone built, and no
work is admitted until memory answers. A head’s write made meanwhile
is refused, not queued: run the verb again once memory is back.
The body has no memory
Section titled “The body has no memory”hale dna run: no memory: HALE_DNA_MEMORY_DSN_SPINE is not set (`hale dna dev` applies memory's schema from HALE_DNA_MEMORY_DSN_OWNER or dna/compose.yaml and hands the host the spine's DSN); nothing is projected or admittedUnder hale dna run the body is given the spine role’s DSN, which
hale dna memory migrate prints. Set it. Under hale dna dev, memory
comes from dna/compose.yaml, and a line before this one says why it
did not come up:
hale dna dev: dna/compose.yaml is here but `docker compose` is not on PATH; set HALE_DNA_MEMORY_DSN_OWNER to a Postgres of your ownhale dna dev: no dna/compose.yaml and no HALE_DNA_MEMORY_DSN_OWNER: there is no memory to run on (`hale dna upgrade` writes the compose file)Once the ledger is adopted, a body without memory does not start:
hale dna run: this record's operations live in the ledger (adopted at …), and no memory is named here (HALE_DNA_MEMORY_DSN_SPINE); a body without its memory admits nothing — set it, or run under `hale dna dev`The schema is another toolchain’s
Section titled “The schema is another toolchain’s”hale dna run: memory: memory schema … is at version …; this toolchain needs version …; run `hale dna memory migrate` with the owner's DSN in HALE_DNA_MEMORY_DSN_OWNERAlso … has no schema version … when nothing migrated it. Run
hale dna memory migrate with the owner’s DSN. A migration never goes
backwards:
… memory schema … is at version …, newer than this toolchain's …; it is not migrated backUpgrade the toolchain instead.
A context package would be stale
Section titled “A context package would be stale”memory's projection is at row …, before the record's knowledge row …; the spine applies it on its tickA piece of work asked memory for its context, and memory had not yet applied a knowledge row the record holds. It waited 20 seconds, then refused rather than hand over a stale package. Check that a body is running with memory and that its sync completes. A body that cannot share the record says so:
hale dna run: memory: projection waits for the record to be shared (the sync did not complete)A write the ledger refused
Section titled “A write the ledger refused”hale dna: `… …` was not written to the ledger: …Memory’s insert function refused the row, and the rest of the line says why. The reasons include:
… retired from this organism (person.retired at row …): the person named has left.task … is handed to …, not to …: only the assignee closes their task.fenced: the lease … is not held, or… expired at … (epoch …) and was not renewed: the write named a body lease that is no longer live.the role … does not write as …: a head writes as its owner's members: over a shared record, this head’s DSN is another owner’s. Check whichHALE_DNA_MEMORY_DSN_HEADit was given.`…` is a row of the record, not the ledger: a record kind was asked of the ledger.
An adoption that has not happened yet
Section titled “An adoption that has not happened yet”history shows ledger.adopting and no ledger.adopted. The
adoption was asked for and no node has carried it out: none has run
since, or memory went away mid-copy. A node picks it up on its next
tick. To leave the organism on the record alone instead,
hale dna ledger abandon --why <why> asks a node to empty the ledger.
Protected evidence
Section titled “Protected evidence”no receipt key: memory keeps no protected evidence until hale dna memory migrate with HALE_DNA_RECEIPT_KEYA customer or confidential body was filed or read, and memory holds no
key to seal it under. Run hale dna memory migrate with
HALE_DNA_RECEIPT_KEY (sixteen characters or more) in the owner’s
environment. A second, different key is refused, because what the
first sealed would no longer open:
memory already seals protected evidence under another receipt key; bodies sealed under it would no longer open, so it is not replacedA body the record redacted cannot come back, in git or in memory:
receipt file: … was redacted; a redacted body is not filed again… was redacted …; a redacted body is not kept againA receipt.withheld row where you expected receipt.classified
means a protected body was produced where no memory was named. The
record keeps its digest and class; no body exists anywhere. Produce the
evidence again where memory is.
The nerves
Section titled “The nerves”The nerves are NATS JetStream. See The nerves.
The body hears nothing
Section titled “The body hears nothing”hale dna run: no nerves: HALE_DNA_NATS_URL_SPINE and HALE_DNA_NATS_ORG are not set (`hale dna dev` creates the stream from HALE_DNA_NATS_URL_OWNER or dna/compose.yaml and hands the host the spine's); the organization hears nothing the record asksThe body runs, and every ask stays unanswered. Under hale dna run,
set the two variables hale dna nerves migrate prints. Under
hale dna dev, a line before this one says why the nerves did not
come up:
hale dna dev: dna/compose.yaml is here but `docker compose` is not on PATH; set HALE_DNA_NATS_URL_OWNER to a NATS server of your ownhale dna dev: no dna/compose.yaml and no HALE_DNA_NATS_URL_OWNER: there are no nerves to run on (`hale dna upgrade` writes the compose file)A server of your own must run JetStream:
nerves migrate: the server does not run JetStream (start it with -js)The organization does not come to read
Section titled “The organization does not come to read”hale dna run: the organization did not come to read the nerves within 20s; the facts wait in the streamThe organization’s connection cannot reach the server, or its user
may not read its durable consumer (spine, or spine_<owner> over a
shared record; dna/nats.conf lists the users). Its log,
.hale/dna/org.log, says which. Nothing is lost: the facts wait in the
stream until it connects.
A publish the stream did not take
Section titled “A publish the stream did not take”hale dna run: nerves: …; stopping for the unit to start this node againThe stream did not acknowledge a publish in time. The body records a
violation.recorded row (adapter_undeliverable), stops and exits
75. A provisioned body’s systemd unit starts it again, and every
request still unanswered is relayed again. Under hale dna dev there
is no unit: start it again yourself. If the stream itself is gone,
hale dna nerves migrate makes it.
The heart’s pull is refused
Section titled “The heart’s pull is refused”hale dna run: the heart's pull on its durable … was refused … time(s) while the connection held: the durable, or its stream, is gone; it is made againRecorded once per outage as violation.recorded (pulse_stopped).
The body makes the durable again and goes on. hale dna board lists
the latest five violations.
The spine
Section titled “The spine”See The spine for the budget, the workflow engine and the body lease.
The budget is spent
Section titled “The budget is spent”… budget exhausted (spent … of … micro-dollars this day in … call(s))task create is refused with this, history shows
budget.exhausted, and nothing model-backed runs until the window
turns. The Leader’s decision on a Review is refused by the same gate
(attempt.allowance_refused on review:<id>), so the Review waits
for the Board; answer it yourself with hale dna review <id> approve.
A leg’s allowance is refused the same way. The allowance is
org_budget() in dna/org/models.hl:
fn org_budget() -> dna::BudgetPolicy { return dna::BudgetPolicy { window: "day", allowance_micros: 25000000 };}Raise it in a reviewed change, or wait for the next window.
An intent noted and never re-offered
Section titled “An intent noted and never re-offered”history shows intent.unrecovered:
offered before a restart and no Task was born; not re-admitted — ask againWork may already have run for it, so it is never offered twice. Ask
again with hale dna task create if you still want it.
An effect whose outcome is unknown
Section titled “An effect whose outcome is unknown”An attempt claimed by an uncertain performer that never reported is
unresolved: its effect may or may not have happened, and no program
retries it. A person decides:
hale dna effect resolve attempt:<id> --outcome ok|failedThe heart and the body
Section titled “The heart and the body”The body lease is held elsewhere
Section titled “The body lease is held elsewhere”A record admits one body. A body that starts while another holds the lease stops at once, with exit code 3:
hale dna run: a body for this record is live on … (ticked …, lease expires in …s); a record admits one body — `hale dna body claim --force` takes it over when that body is gonehale dna body says who holds it. If that body is gone (its machine
died, its process was killed), take the lease:
hale dna body claim --forceWithout --force, a claim on a live lease is refused:
hale dna: body: live on …, ticked … ago; a record admits one body. `hale dna body claim --force` takes the lease from a body that is gone (it stops itself when it next asserts the lease)The body that lost its lease stops when it next asserts it:
hale dna run: the body lease is no longer mine: held by …; a record admits one body — stoppingReleasing another clone’s lease also needs --force:
hale dna: body: the lease is held by …, not this clone (…); `hale dna body release --force` releases another'sA body that cannot reach the record’s remote does not start, because it cannot prove a lease:
hale dna run: the record's remote cannot be reached, so the body lease cannot be taken; a body runs only under a lease it can proveA change that did not survive
Section titled “A change that did not survive”A Mutation that rolled_back: history shows expression.crashed
with the exit code (on a fleet, the instance and node), then
mutation.rolled_back. The expression exited inside its observation
window; the genome is back at the base and the old expression runs.
…: never expressed by …A touched instance never reported instance.up at the new revision in
time. Look at that node’s terminal: the revision may not have fetched,
or the seed may not build there.
…: denied: candidate breaks the fleetThe candidate’s services compose, but a claim the fleet plan makes over
them fails. The mutation.deny row names the failing steps.
mutation.refused with candidate moved after review: … != …: the
sandbox’s commit changed between the review and the apply. Nothing was
applied; ask again, and the new candidate gets its own Review.
No fleet
Section titled “No fleet”no `[dna] fleet = "<name>"` in hale.toml: the DNA expresses no fleet herehale dna fleet, deploy and rollback need to know which declared
fleet to express. Add the section to hale.toml; see
The heart and the body.
Senses and reflexes
Section titled “Senses and reflexes”See Senses and reflexes.
hale dna senses up: dna/compose.yaml is here but `docker compose` is not on PATHhale dna senses up: no dna/compose.yaml: there is no store for the senses (`hale dna upgrade` writes the compose file)The senses’ store is the senses service of dna/compose.yaml.
Under hale dna dev the same reason comes as hale dna dev: senses: …,
and nothing else stops for it.
reflexes: HALE_DNA_NATS_URL_REFLEXES, HALE_DNA_NATS_VAULT_REFLEXES and HALE_DNA_NATS_ORG name the nerves this program fires ontoreflexes: no store (HALE_DNA_SENSES_URL); nothing to readreflexes: the vault holds no credential … (`hale dna upgrade` provisions the organism's secrets)The reflexes refuse to start without their nerves, their store, or
their credential. hale dna nerves migrate prints the first three
variables, and hale dna senses up the store’s URL.
The skin
Section titled “The skin”See The skin for the vault and its slots.
A secret is missing
Section titled “A secret is missing” MISSING forge-token the forge's token — a person supplies it: `hale dna secret set FORGE_TOKEN` MISSING model-OPENAI_API_KEY the model's key OPENAI_API_KEY — a person supplies it: `hale dna secret set OPENAI_API_KEY`hale dna secrets lists every secret the organism needs and marks the
ones the vault lacks. A secret the organism draws itself (memory’s
roles, the nerves’ accounts, a loopback OIDC client) says
— `hale dna upgrade` draws it. A slot is yours to fill, from
stdin:
hale dna secret set OPENAI_API_KEYAdd --body <user@host> to fill the vault where the body runs. The
board repeats what is missing:
body: no credential for the model (none of … is in the vault where the body runs); `hale dna secret set <NAME> [--body <user@host>]` puts one theresecret: the forge's token is not in the vault (forge-token); `hale dna secret set FORGE_TOKEN` fills itA model key is read from its vault slot model-<NAME> only, never
from the environment. With none, a call is refused before it is made,
and the model.called row in history says credential not present.
A secret the verb will not take
Section titled “A secret the verb will not take”secret set: `…` is no slot of the organism's (FORGE_TOKEN, OIDC_CLIENT_SECRET, or a credential dna/org/models.hl names: …); `hale dna secrets` lists themsecret set: the value never goes on the command line (it would sit in every shell history and process list); name the variable alone and give the value on stdinsecret rotate: … was never set (`hale dna secret set …` first)Name the slot alone, give the value on stdin, and set a secret before you rotate it.
A drawn secret is gone
Section titled “A drawn secret is gone”nerves migrate: the vault holds no credential for the nerves' … (`hale dna upgrade` provisions the organism's secrets; `hale dna secrets` lists them)the compose database's password is not provisioned (postgres-owner-… in the vault, dna/postgres.secrets beside compose; `hale dna upgrade` provisions them, `hale dna secrets` lists them)Run hale dna upgrade, which draws what the vault lacks. A password
file (dna/postgres.secrets, dna/nats.secrets.conf) is never
written where git would track it:
refused: … is not ignored by git, and a password is never written where git would track it (add /… to .gitignore; `hale dna upgrade` does)refused: … is tracked by git, and a password is never written into a tracked file (`git rm --cached …`)Violations
Section titled “Violations”violations: … recorded, each absorbed by its owner (the latest last)A violation.recorded row is a closure a part absorbed and went on
from: adapter_undeliverable and pulse_stopped
(The nerves), lease_unsettled (Legs).
hale dna board shows the latest five.
hale dna work prints one JSON object per verb; a refusal has
"state": "refused" and the reason. See Legs, hands and
voice.
A performer with no effect class
Section titled “A performer with no effect class”hale dna work: dna/org/work.hl: the … performer declares no effect class; every performer declares one: effect_free, idempotent or uncertainEvery performer in dna/org/work.hl declares effect_free,
idempotent or uncertain. Until it does, the verbs that claim or
hand back an outcome refuse to start; the others still read, settle,
file friction and drain.
Nothing to claim
Section titled “Nothing to claim”no admitted attempt awaits a … leg with capabilities `…`: … of that kind outstanding, …hale dna work next found nothing it may take, and says why for each
attempt it passed over: performed in the organism, awaiting the owner,
needing a capability the leg lacks, not admitting a performer of effect class …, of a data class the leg may not see, or unresolved
(An effect whose outcome is unknown).
The lease is gone
Section titled “The lease is gone”stale: the lease on attempt … expired at …stale: the lease on attempt … is …'s at token …, not …'s at token …the lease on attempt … was taken as a performer of effect class `…`; an outcome under it names that class, not `…`An outcome, a renewal or an allowance under a lease that is no longer
this leg’s. Renew before the lease ends (hale dna work renew). An
outcome names the effect class the lease was taken with.
When a lease expires with no outcome, the owner records a
violation.recorded row (lease_unsettled) once for that lease, and
asks the attempt again:
the lease … held at token … expired at … with no outcome; the attempt is asked againNo allowance
Section titled “No allowance”The budget’s one gate answers hale dna work allowance. On a spent
window it refuses with budget exhausted (…)
(The budget is spent). Within an attempt, a
call that would pass what is left is not made:
the call would cost … micro-dollars, past what is left of the attempt's allowance (… of …): no call was madethe attempt's allowance of … micro-dollars is spent (…); the budget gate admits no further callSchedules
Section titled “Schedules”See Schedules.
hale dna: schedule declare: refused: schedule …: …The organization refused the declaration and said why. The reasons are:
nobody holds position:…: it convenes nothing until someone does (`hale dna fill … <holder>`): fill the convening position first.no definition `…` in the catalog:hale dna definitionslists the catalog.… takes `…`, which the inputs do not name: pass every input the definition takes in--args.its inputs are a JSON object:--argsis a JSON object.cron `…`: a cron expression has five fields (minute hour day-of-month month day-of-week), not ….… holds neither the Board nor position:…: declaring a schedule is the Board's, or the convener's own holder's.
hale dna: schedule declare: --every … is no interval (a number and ms, s, m, h or d)hale dna: schedule pause: no schedule `…` is declared in the recordhale dna schedule lists what the record declares, with each
schedule’s occurrences, skips and misses.
The head and the face
Section titled “The head and the face”The head will not start
Section titled “The head will not start”hale dna ui: trusted-local is a fixture's mode (HALE_DNA_TRUSTED_LOCAL=1); a project serves its head under OIDC: `git config dna.principal oidc` with dna.oidc.issuer, dna.oidc.client and dna.oidc.member (dna/face/start.sh sets them up with the stub provider, dna/oidc)A head needs a principal source and refuses to start without one. Name the OIDC issuer in the record’s git config, and put the client’s secret in the vault:
git config dna.principal oidcgit config dna.oidc.issuer https://issuer.examplegit config dna.oidc.client acme-headgit config dna.oidc.redirect https://head.example/callbackgit config --add dna.oidc.member "<subject>=alice"hale dna secret set OIDC_CLIENT_SECRETThe secret goes to the vault as oidc-client-<client>. The head checks
its configuration before it serves:
hale dna ui: dna.principal is oidc, so dna.oidc.issuer, dna.oidc.client and dna.oidc.redirect must be sethale dna ui: the issuer must be https (plain http only on this machine): …hale dna ui: the issuer … is not usable: …An issuer on the loopback is not usable until the record pins its key
(git config dna.oidc.key <SubjectPublicKeyInfo, base64>).
A verdict refused
Section titled “A verdict refused”review … refused the verdict: …The Review did not admit the verdict, and says why:
digest mismatch: … != …: the verdict named a candidate other than the one under review (the sandbox changed, or a--digestwas stale). Render it again withhale dna review <id>and answer the digest it shows.authority … does not satisfy …: the Review needs a higher authority (boardfor a Board-class change).reviewer … authored the candidate: someone other than the change’s author signs it. A governance proposal requiring the Board (a fill, an opening) admits its author; a change to the work never does.reviewer … holds no position this Review requires (…): a routed Review admits only holders of the positionshale dna routenames.
hale dna: review: a verdict cites no gate run; the host observes each gate's run at the forge (`hale dna github sync`)A verdict does not carry gate evidence. The host reads each gate’s run at the forge.
Every Review needs the Board
Section titled “Every Review needs the Board”status shows [pending] needs board — … on each Review. That is the
default posture: an ask is an application change, and the Leader’s
grant in dna/org/main.hl covers refactor docs. Decide with
hale dna review <id> approve, or widen the grant in a reviewed
change; see Shaping and governing it.
A person’s task that never settles
Section titled “A person’s task that never settles”A handed Task closes only when its assignee reports it with
hale dna task done <id> --as <them>, carrying the evidence or the
authorized exception its acceptance practice requires. A restart does
not close it, and neither does anyone else’s report. hale dna board
lists the tasks waiting.
Starting over
Section titled “Starting over”Deleting the record’s refs deletes the record; git log still holds
every change it applied. hale dna init then seeds a fresh record from
the program as it is now, and keeps the files already there:
git for-each-ref --format='%(refname)' refs/dna | xargs -n1 git update-ref -dhale dna init .Where each piece of state lives is in the Reference.