> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bitfield.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Find which Runtime Kit boundary broke: package admission, named data reads, target requests, replies, local state, or activation.

<div className="bf-article">
  <p className="bf-lead">
    When a Runtime Kit feature breaks, do not guess. Identify which public link failed: package file, named data read, target request, reply decoding, local state, or activation.
  </p>

  ## What this is

  Runtime Kit deliberately hides private implementation details from app code. That is good for product architecture, but it means debugging should follow the public chain instead of poking unpublished code.

  Use this chain:

  ```text theme={null}
  package file
    -> package admission
    -> named data read input
    -> React surface
    -> target request
    -> reply decoding
    -> local state and activation
  ```

  <div className="bf-flow" aria-label="Troubleshooting flow from symptom to verified fix">
    <div className="bf-flow-node">
      <span className="bf-flow-step">01</span>
      <strong>Symptom</strong>
      <p>Name what the user sees, not what you assume broke.</p>
    </div>

    <div className="bf-flow-arrow" aria-hidden="true">→</div>

    <div className="bf-flow-node">
      <span className="bf-flow-step">02</span>
      <strong>Public link</strong>
      <p>Pick boundary, read, request, reply, local state, or activation.</p>
    </div>

    <div className="bf-flow-arrow" aria-hidden="true">→</div>

    <div className="bf-flow-node">
      <span className="bf-flow-step">03</span>
      <strong>Evidence</strong>
      <p>Capture the safe name, payload shape, state, or error.</p>
    </div>

    <div className="bf-flow-arrow" aria-hidden="true">→</div>

    <div className="bf-flow-node">
      <span className="bf-flow-step">04</span>
      <strong>Verify</strong>
      <p>Fix one link and prove the expected state appears.</p>
    </div>
  </div>

  Do not skip straight to private files. First prove which public link failed.

  ## Start with the symptom

  | Symptom                                            | Most likely link                         | First evidence to capture                                          |
  | -------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------ |
  | Package never becomes available.                   | Package file or package admission.       | Package name, package set, boundary filename, boundary JSON error. |
  | Component stays empty.                             | Named data read input.                   | Input name, component states, whether `data === null`.             |
  | Component throws immediately.                      | Missing read scope or unavailable input. | Exact error message and component location.                        |
  | Button call throws before work runs.               | Target request shape.                    | Target name and payload shape.                                     |
  | Button call succeeds but UI cannot use the result. | Reply decoding.                          | Reply byte format and decoder code.                                |
  | Works on one device but not another.               | Activation or package set/local state.   | Device scope and account activation state.                         |
  | App code imports deep Runtime Kit files.           | App boundary violation.                  | Import list and files changed.                                     |

  Find the link first. Then fix that link.

  ## First five minutes

  Do this before editing code:

  | Minute | Action                                                                | What you learn                                     |
  | ------ | --------------------------------------------------------------------- | -------------------------------------------------- |
  | 1      | Write the package set, package name, prepared input, and target name. | Whether everyone is debugging the same feature.    |
  | 2      | Check the package file exists and names the record or slot.           | Whether the package can be admitted.               |
  | 3      | Check the component renders loading, error, empty, and success.       | Whether the UI is hiding the real state.           |
  | 4      | Check the request payload and reply decoder.                          | Whether the button and target agree on a contract. |
  | 5      | Check whether the issue is one device or every device.                | Whether activation/local state is involved.        |

  If you cannot fill in the contract, that is the bug. Write the contract first, then debug the link that does not match it.

  ```md theme={null}
  ## Runtime Kit contract

  - Package set:
  - Package:
  - Prepared input:
  - Target:
  - Request payload:
  - Reply payload:
  - Device scope: one device or every device
  ```

  ## Package file failures

  Check the boundary file before blaming React.

  Valid packages have one boundary file:

  ```text theme={null}
  things-to-store-and-run.json
  ```

  The package should declare supported thing types:

  | Thing type     | Common failure                                                |
  | -------------- | ------------------------------------------------------------- |
  | `record`       | The record has no payload action or multiple payload actions. |
  | `stored_bytes` | The file path does not stay inside the package.               |
  | `slot`         | The slot has no boundary or no method list.                   |

  Fix package file problems in the package. Do not work around them in the app surface.

  Fast boundary checklist:

  ```text theme={null}
  folder exists
  boundary file is named things-to-store-and-run.json
  JSON parses
  top-level package field is non-empty
  things is an array
  each thing has a supported type
  package-owned paths stay inside the package folder
  ```

  Verify the fix:

  | Fix                        | Verification                                                |
  | -------------------------- | ----------------------------------------------------------- |
  | Add missing boundary file. | The package folder contains `things-to-store-and-run.json`. |
  | Fix unknown thing type.    | The boundary uses only supported public thing types.        |
  | Fix escaped file path.     | Every package-owned file path stays inside the package.     |
  | Fix missing slot boundary. | The slot declares its public call shape and methods.        |

  ## Named data read failures

  If `useBitfieldData(...)` does not return the value you expected, check the input name and surface contract.

  ```tsx theme={null}
  const welcome = useBitfieldData<WelcomeCopy>('welcome-copy');
  ```

  Ask:

  | Question                                                  | Why it matters                                       |
  | --------------------------------------------------------- | ---------------------------------------------------- |
  | Is `welcome-copy` the public input name for this surface? | Selector names are not storage paths.                |
  | Does the component render `loading`?                      | The value may not be ready yet.                      |
  | Does the component render `error`?                        | A hidden thrown error becomes a bad user experience. |
  | Does the component handle `data === null`?                | Empty is a real state.                               |
  | Did app code import only the public React hook?           | Deep imports bypass the boundary.                    |

  The correct fix is usually to repair the package/surface contract or the component states, not to create a private read path.

  Read failures split into two different problems:

  | Problem                     | What it looks like                                                                  | Correct fix                                               |
  | --------------------------- | ----------------------------------------------------------------------------------- | --------------------------------------------------------- |
  | The input is not available. | Loading never resolves or Runtime Kit reports unavailable input.                    | Fix the package/surface contract that prepares the input. |
  | The UI hides the state.     | The hook returns loading, error, or null but the component renders nothing helpful. | Render all states visibly.                                |

  Do not solve either problem by reading local files from the component. That creates a second data path and hides the real broken link.

  Verify the fix:

  ```text theme={null}
  The component shows loading while waiting.
  The component shows a visible error when Runtime Kit reports one.
  The component shows an empty state when data is null.
  The component renders real data when the prepared input is available.
  ```

  ## Missing read scope errors

  If you see an error like:

  ```text theme={null}
  useBitfieldData() without an input handle requires a Bitfield read scope
  ```

  The component is trying to read a prepared input without the app surface that provides it.

  Do not fix this by importing private provider or scope code into the component. The surface owner must provide the read scope before the component renders, or the component must receive an input handle through the public shape that surface supports.

  ## Target request failures

  The request function needs a non-empty public action name.

  ```ts theme={null}
  await sendRequestToBitfieldTarget({
    target: 'welcome.suggest',
    payload: { topic: 'launch' },
  });
  ```

  Check:

  | Question                                      | Why it matters                                                 |
  | --------------------------------------------- | -------------------------------------------------------------- |
  | Is `target` a non-empty string?               | Runtime Kit rejects empty target names.                        |
  | Does the package declare that target?         | App code can only call public doors.                           |
  | Is the payload shape what the target expects? | Runtime Kit sends bytes; the target owns meaning.              |
  | Should this request be cancellable?           | Search, typeahead, and long work should use `AbortController`. |

  Do not import the target implementation to "make the button work." That bypasses Runtime Kit.

  Request failures split into four common cases:

  | Case           | What to check                            | Fix                                                                      |
  | -------------- | ---------------------------------------- | ------------------------------------------------------------------------ |
  | Empty target   | `target` is missing or `''`.             | Pass the public action name declared by the package.                     |
  | Unknown target | The package does not declare that door.  | Add or fix the `slot` declaration in the package file.                   |
  | Bad payload    | The target expects another shape.        | Update the caller or action reply shape, then document the chosen shape. |
  | Stale request  | Old work returns after newer user input. | Use `AbortController` when stale work matters.                           |

  Verify the fix:

  | Check          | Pass condition                                                                     |
  | -------------- | ---------------------------------------------------------------------------------- |
  | Target name    | Non-empty string and declared by the package.                                      |
  | Payload        | Matches the public action reply shape.                                             |
  | Abort behavior | Search, typeahead, and long work use an abort signal when stale work matters.      |
  | App boundary   | Button imports `sendRequestToBitfieldTarget(...)`, not target implementation code. |

  ## Reply decoding failures

  The reply is bytes:

  ```ts theme={null}
  const reply = await sendRequestToBitfieldTarget({
    target: 'welcome.suggest',
    payload: { topic: 'launch' },
  });
  ```

  Decode according to the action reply shape:

  ```ts theme={null}
  const text = new TextDecoder().decode(reply.payload);
  const data = JSON.parse(text);
  ```

  If JSON parsing fails, the request may still have succeeded. The problem may be that the target returned text, binary bytes, an error payload, or a different JSON shape than the app expected.

  Write the reply shape beside the caller:

  ```ts theme={null}
  type SuggestionReply = {
    suggestion: string;
  };
  ```

  Then decode to that contract only after the target promises JSON:

  ```ts theme={null}
  const text = new TextDecoder().decode(reply.payload);
  const data = JSON.parse(text) as SuggestionReply;
  ```

  If the target returns binary data, do not force it through JSON. Keep the reply as bytes and document the binary format.

  Debug reply failures with this order:

  1. Decode the bytes as text only if the target promises text or JSON.
  2. Parse JSON only if the target promises JSON.
  3. Check whether the reply is an error payload with a different shape.
  4. Check whether the target changed its public reply shape.
  5. Update either the action reply shape or the decoder, not both blindly.

  ## Payload conversion surprises

  Runtime Kit converts payloads before sending:

  | Payload                           | Sent as         |
  | --------------------------------- | --------------- |
  | `Uint8Array`                      | The same bytes. |
  | `string`                          | Text bytes.     |
  | object, array, number, or boolean | JSON bytes.     |
  | `null` or omitted                 | Empty bytes.    |

  That last row is easy to miss. If your target needs a JSON `null`, send an object with an explicit field instead, such as `{ "value": null }`.

  ## A record reads back empty even though you just wrote it

  A record's **address mode** is part of its identity. Read it the same way it was written, or the engine will not find it — even though the bytes are present.

  | Address shape                            | Stored as     |
  | ---------------------------------------- | ------------- |
  | `package::thing::name` (a `::` scheme)   | label-hash    |
  | `partition\|thing\|name` (contains `\|`) | identity-text |

  A workflow `write` step that targets a `::` address with no explicit address mode stores it **label-hash**. If a reader (or a producer poll) looks for that address in identity-text mode, it gets "not found" forever. When a value "won't read back," read it at **both** modes to discover which one the writer used, then make the reader match.

  ```text theme={null}
  Symptom: write succeeds, read returns empty/not-found.
  Check: did the writer use a `::` address (label-hash) and the reader poll identity-text (or vice-versa)?
  Fix: make the reader's address mode match the writer's.
  ```

  ## A surface I removed still shows in the sidebar

  Removing a `placeable-surface` (or any current-state record) from `things-to-store-and-run.json` and redeploying with the live-log-only path does **not** remove it from the running app. That deploy is **additive** — it writes the records present in your source; it never retracts records you deleted from source.

  To retract an already-admitted record, add an explicit tombstone to your source and redeploy:

  ```json theme={null}
  {
    "type": "record",
    "address": "package::your-package::placeable-surface::old-surface",
    "address_mode": "identity_text",
    "path": ["current-item", "published-record"],
    "remove": true
  }
  ```

  The deploy then emits a removal for that exact address. Verify the record is gone by reading the address directly (it should report not-found). Keep the tombstone in source — it is the durable record that this address is retired.

  ## A loading card (AI producer) is stuck "Working…" forever

  A board/producer surface dispatches an action, then **polls** a result address to know when it is done. If the poll's address mode does not match where the action actually wrote its result, the surface never sees the result and shows its loading copy forever — even though the action finished successfully and wrote a real record.

  ```text theme={null}
  Symptom: producer card spins on "Working…" indefinitely; the action actually completed.
  Check: read the producer's result address at both address modes.
  Fix: set the producer's poll address mode to the one that returns the record.
  ```

  This is the same root cause as "reads back empty": the result was written one way and read another. It is not a stuck engine.

  ## Local state confusion

  Customer-visible Bitfield state lives under:

  ```text theme={null}
  ~/.bitfield/
    stored-data/
    runtime-kit/
    check-license/
  ```

  Use this meaning:

  | Area             | Debug meaning                                                        |
  | ---------------- | -------------------------------------------------------------------- |
  | `stored-data/`   | Durable Bitfield data area. Do not edit by hand.                     |
  | `runtime-kit/`   | Package sets and current Runtime Kit process state.                  |
  | `check-license/` | Device permission material. Use account flows for activation issues. |

  Do not copy one device's local state to another device as a fix. Device permission and package state need the right owner flow.

  Safe support note:

  ```text theme={null}
  It is OK to say which top-level area seems involved: stored-data, runtime-kit, or check-license.
  Do not paste file contents from those folders into public channels.
  Do not copy those folders between devices.
  ```

  ## Boundary failures

  Check for these violations before debugging private files:

  | Violation                                   | Fix                                                        |
  | ------------------------------------------- | ---------------------------------------------------------- |
  | Imports from unpublished Runtime Kit paths. | Replace with public imports only.                          |
  | React component parses package files.       | Move package material to the boundary and read named data. |
  | Button imports target implementation.       | Call `sendRequestToBitfieldTarget(...)`.                   |
  | Component ignores loading/error/empty.      | Add all four states.                                       |
  | Target reply is parsed without a contract.  | Decode according to the target's public reply shape.       |

  Code tends to connect things directly when the boundary is not repeated clearly. Use [Build with AI agents](/runtime-kit/ai-build-rules) only when the feature is being generated by an AI agent.

  Use this note when a change crosses the Runtime Kit boundary:

  ```text theme={null}
  This crosses the Runtime Kit boundary. Keep app code on public imports only.
  Name the prepared input or target instead of importing package implementation code.
  Show loading, error, empty, success, and request-result states.
  ```

  ## Escalation checklist

  Before asking for support, capture the public facts:

  1. The package name.
  2. The package set name.
  3. The public input name that failed.
  4. The public action name that failed.
  5. The payload shape, without secrets or customer data.
  6. The visible error message.
  7. Whether the problem happens on one device or every device.

  Do not paste local state contents, device permission files, private keys, tokens, or customer data into public support channels.

  ## Quick reference

  ```text theme={null}
  No data       -> check prepared input and render states
  Request fails -> check target name and payload shape
  Decode fails  -> check reply shape
  Device differs -> check activation and package set
  Boundary crossed -> remove private imports and direct implementation calls
  ```

  ## Now build the bigger version

  Add a small public debug note beside every Runtime Kit feature.

  ```md theme={null}
  ## Runtime Kit contract

  - Package set: customer-portal
  - Package: launch-assistant
  - Prepared inputs: welcome-copy, launch-checklist
  - Targets: launch.next-step
  - Request shape: { currentStep: string }
  - Reply shape: { nextStep: string; reason: string }
  ```

  When something breaks, compare the bug to that contract. If the prepared input is missing, fix the package/surface link. If the target reply has a different shape, fix the action reply shape or the decoder. If one device behaves differently, check activation and package-set state before touching the UI.

  That gives support and developers the same map without exposing local state contents or private implementation files.
</div>
