Diagnostics
The two channels the engine reports on, the event that carries them to Blueprint, every code the runtime emits, and the sync codes you act on.
All documentation — Reference
Start here
Concepts
Authoring for a build
Playing a story
The generated assets and code
The plugin and sync
On this page
The engine does not fail when the project data is wrong. It carries on, and it says so — on two channels, for two different readers.
| For | Carries | |
|---|---|---|
| On Error Raised — an event | your game, deciding what the player sees | a message, a board id and a node id |
| On Diagnostic — an event over the diagnostics channel | your developer, deciding what to fix | a level, a stable machine code, a message, and a field key as well |
Every error reaches both. The diagnostics channel additionally carries warnings and information that raise no error event at all.
The error
Message, Board Id, Node Id. Both ids are empty when the error is about the host’s own code rather than a place in the story — a policy mistake is about the whole session and names no node.
Raised on the session’s On Error Raised, and re-raised by the TaleShard Player component. With nothing bound, an error goes to the log.
The diagnostic
| Pin | Notes |
|---|---|
| Level | Info · Warning · Error |
| Code | Match on this, never on the message. Messages are written for a human and change |
| Message | |
| Board Id · Node Id · Field Key | each empty when the diagnostic is not about one |
| Level | Means |
|---|---|
Info | something worth knowing. Never used for a failure |
Warning | the engine carried on, but an author would want to know |
Error | the engine could not do what the data asked. Playback continues |
The session’s On Diagnostic carries every entry, at every level, always on the game thread — the session marshals for you. That is the whole Blueprint face; there is nothing to install.
The channel replays the sync’s own findings
A payload carries the diagnostics the sync raised while resolving it. The session replays them into its channel when it is built, so a problem found at sync time — a field the exporter could not read, a reference to something that has moved — reaches you at startup rather than never.
For a C++ host holding the engine directly
A session built through the plugin’s own types needs none of this. A C++ host that constructs the engine itself hands it a sink, and three ship: one that collects everything in memory (the default when none is named — silence has to be asked for), one that forwards each entry to a callback, and one that drops everything.
Every code the runtime emits
Grouped by what went wrong. Match on the code; the message is prose.
The graph could not be walked
| Code | Level | |
|---|---|---|
no-start-board | Error | the project names no start board |
no-entry-point | Error | a board with no way in |
node-missing | Error | an edge pointing at a node that is not there |
unsupported-node-type | Error | the view cannot read this node’s type at all. Playback takes the first exit and says how many branches it discarded — refusing to move would strand the player mid-story |
auto-advance-limit | Error | a loop in the graph, or a chain deeper than the cap |
jump-field-missing | Warning | a jump node whose target field is empty |
jump-board-has-no-entry | Warning | a jump into a board with no way in |
call-frame-node-missing | Warning | a return to a caller whose node no longer exists |
engine-busy | Warning | a verb arrived while the engine was mid-transition — the one you get for calling Advance from inside a step handler, and for Next Line asked from a diagnostics listener while the engine is drawing — a step, before it reaches its node handler, or the history again (Set Language, Redraw, Restore). Next Line from a node handler is answered |
redraw-superseded | Warning | a diagnostics listener called Start, Restore Playthrough or Stop while Set Language or Redraw was re-drawing the history. The re-draw is abandoned: a new playthrough is left exactly as the listener started it, and a stopped one keeps its live state |
restore-superseded | Warning | the same, while Restore Playthrough was drawing its window. The restore is abandoned and nothing is presented; its save-load report describes the save it would have loaded, not the story the player is on |
A script, a gate or a requirement
| Code | Level | |
|---|---|---|
script-unparsed | Error | the expression did not parse at sync time. Recorded, never a crash and never a silent success |
script-failed | Error | the runtime reported a failure |
gate-unparsed | Error | |
gate-failed | Error | the gate could not be evaluated. It reads as closed |
requirement-unparsed | Error | |
interpolation-failed | Warning | a {…} chip in a field or a choice could not be worked out: it did not parse at sync time, it names an expression the project data does not carry, or its evaluation failed. The chip shows its literal {source}. Once per chip and reason per session |
binding-unresolved-expression | Warning | an expression with no usable tree. A gate that will refuse at play time |
binding-unresolved-named-expression | Warning | a condition variable that cannot be evaluated. It reads as absent forever |
binding-unresolved-name | Info |
A command
| Code | Level | |
|---|---|---|
command-failed | Error | the handler called Fail. An awaited command whose event your game has not overridden fails this way, naming the event to override |
command-not-implemented | Warning | an ordinary command ran whose event your game has not overridden. Once per command per session, naming the event to override. Reported by the generated command class, not the engine |
command-restart-limit | Error | one pump followed a playthrough restarted from inside a command handler too many times |
command-result-refused | Error | the answer exceeded the size budget |
command-result-unread | Warning | it answered, and no result variable was declared to put the answer in |
command-tag-unregistered | Warning | a registration named a command tag the project does not declare |
command-completed-off-thread | Info | Refused. The story is still waiting |
command-completed-late | Info | it settled after the playthrough moved on. Nothing was changed |
command-completed-twice | Info | only the first answer counted |
A verb was refused
| Code | Level | |
|---|---|---|
choice-not-takeable | Warning | Choose named an option the player can see but cannot take |
save-refused | Warning | Save while nothing has been presented, or while a command is in flight |
Every refused verb also answers false with the same reason on its own pin. The diagnostic is the copy for the log.
A save
| Code | Level | |
|---|---|---|
save-node-missing | Error | the save stands on a node that no longer exists |
What a successful load had to change is not a diagnostic at all — it is the report, because it is something to show a player.
The payload
| Code | Level | |
|---|---|---|
duplicate-entity-code | Warning | two entities share a code. The first wins; the other is unreachable by code |
duplicate-field-code | Warning | two fields of one node type share a code |
unknown-field-type | Warning | a field type this build cannot name. The raw type name survives beside it |
unknown-field-storage | Warning | |
unknown-ref-kind | Warning | a reference kind this build cannot name |
exit-content-key-missing | Warning | an exit names a content field its own node does not carry |
choice-text-key-missing | Warning | a node lends choice text under a key it does not carry — resolved against the destination, unlike the one above |
quiz-feedback-key-missing | Warning | a question’s answer whose feedback field is not on its node |
item-missing | Warning | an entities-with-counts entry — on an exit’s hand-over, a node field or an entity field — naming an entity the payload does not carry. The entry is dropped, its count and its cell with it; the entries beside it keep their places. A hand-over’s drop is also raised on the error event as FTaleShardDialogueEngine::ItemMissingPlayerMessage, a sentence with no id in it that you can show a player, as the browser player raises it |
A newer sync reaching an older plugin is what the three unknown-* codes exist for: the field or
reference is kept, nameable, and skipped by the typed reads rather than crashing anything.
Your own declarations
| Code | Level | |
|---|---|---|
policy-unknown-node-type | Error | a declared node type matches nothing in this project |
policy-unknown-field-code | Error | a declared field matches nothing |
node-handler-unknown-type | Warning | a node handler names a node type the project does not have. Nothing will ever reach it |
node-handler-missing | Info | a step of a type no handler claims. Once per distinct node type, not once per step |
The policy codes are inert mistakes — the declaration silences nothing — which is why they are reported rather than refused. Both messages print the project’s actual inventory, so the fix is usually in the message itself.
Codes a sync reports
A sync reports on its own channel: the Last sync list of the sync window, and the Output Log. These codes are upper case and numbered, which tells them apart from the runtime’s; a game never receives them. The ones below ask something of you.
| Code | Level | |
|---|---|---|
BP001 | Warning | a Blueprint uses a TaleShard node that no longer matches the project: a field, node type, entity definition, story variable or command the sync removed, or one whose type or arguments it changed. The message names the Blueprint and the node, and the entry has an Open button. The sync compiles that Blueprint, so a removed name shows as a compile error on the node itself — delete the node, or restore the name in TaleShard and sync again. A changed type is refreshed in place |
CPP030 | Error | generated C++ needs GameplayTags, TaleShardCore and TaleShardRuntime in the module’s build rules, and the module’s Build.cs has no PublicDependencyModuleNames.AddRange(new string[] { ... }) line to add the missing ones to. No C++ is written; the story content synced and plays. The message gives the line to add |
CPP034 | Warning | Generate C++ Types is On, and the project has no C++ module of that name to compile the generated files, so none were written. Add a C++ class (Tools > New C++ Class), or choose Automatic or Off |
CPP035 | Info | a generated C++ folder an earlier sync wrote was removed, because no module compiles it |
CPP036 | Info | the sync added the missing dependencies to the module’s Build.cs, which the generated C++ needs. The next build of the project compiles them in |
CPP037 | Error | the sync could not write those dependencies into Build.cs, so no C++ was written. The story content synced and plays. The message gives the line to add by hand |
GEN045 | Warning | the payload asset could not be pointed at the entity index the same sync generated, so the generated C++ entity reads that take a session find nothing until the next sync. The message carries the reason |
MEDIA006 | Warning | media files did not download, usually a network failure. The message names each file and why. The story synced and plays without them, the sync window reads Synced — N media file(s) missing, and the next sync asks for them again |
When a CPP error stops the C++ layer, the sync window reads Synced — C++ not generated and
shows the reason beneath: the assets, the tags and the story are already written.