Skip to content

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
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.

ForCarries
On Error Raised — an eventyour game, deciding what the player seesa message, a board id and a node id
On Diagnostic — an event over the diagnostics channelyour developer, deciding what to fixa 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

PinNotes
LevelInfo · Warning · Error
CodeMatch on this, never on the message. Messages are written for a human and change
Message
Board Id · Node Id · Field Keyeach empty when the diagnostic is not about one
LevelMeans
Infosomething worth knowing. Never used for a failure
Warningthe engine carried on, but an author would want to know
Errorthe 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

CodeLevel
no-start-boardErrorthe project names no start board
no-entry-pointErrora board with no way in
node-missingErroran edge pointing at a node that is not there
unsupported-node-typeErrorthe 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-limitErrora loop in the graph, or a chain deeper than the cap
jump-field-missingWarninga jump node whose target field is empty
jump-board-has-no-entryWarninga jump into a board with no way in
call-frame-node-missingWarninga return to a caller whose node no longer exists
engine-busyWarninga 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-supersededWarninga 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-supersededWarningthe 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

CodeLevel
script-unparsedErrorthe expression did not parse at sync time. Recorded, never a crash and never a silent success
script-failedErrorthe runtime reported a failure
gate-unparsedError
gate-failedErrorthe gate could not be evaluated. It reads as closed
requirement-unparsedError
interpolation-failedWarninga {…} 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-expressionWarningan expression with no usable tree. A gate that will refuse at play time
binding-unresolved-named-expressionWarninga condition variable that cannot be evaluated. It reads as absent forever
binding-unresolved-nameInfo

A command

CodeLevel
command-failedErrorthe handler called Fail. An awaited command whose event your game has not overridden fails this way, naming the event to override
command-not-implementedWarningan 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-limitErrorone pump followed a playthrough restarted from inside a command handler too many times
command-result-refusedErrorthe answer exceeded the size budget
command-result-unreadWarningit answered, and no result variable was declared to put the answer in
command-tag-unregisteredWarninga registration named a command tag the project does not declare
command-completed-off-threadInfoRefused. The story is still waiting
command-completed-lateInfoit settled after the playthrough moved on. Nothing was changed
command-completed-twiceInfoonly the first answer counted

A verb was refused

CodeLevel
choice-not-takeableWarningChoose named an option the player can see but cannot take
save-refusedWarningSave 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

CodeLevel
save-node-missingErrorthe 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

CodeLevel
duplicate-entity-codeWarningtwo entities share a code. The first wins; the other is unreachable by code
duplicate-field-codeWarningtwo fields of one node type share a code
unknown-field-typeWarninga field type this build cannot name. The raw type name survives beside it
unknown-field-storageWarning
unknown-ref-kindWarninga reference kind this build cannot name
exit-content-key-missingWarningan exit names a content field its own node does not carry
choice-text-key-missingWarninga node lends choice text under a key it does not carry — resolved against the destination, unlike the one above
quiz-feedback-key-missingWarninga question’s answer whose feedback field is not on its node
item-missingWarningan 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

CodeLevel
policy-unknown-node-typeErrora declared node type matches nothing in this project
policy-unknown-field-codeErrora declared field matches nothing
node-handler-unknown-typeWarninga node handler names a node type the project does not have. Nothing will ever reach it
node-handler-missingInfoa 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.

CodeLevel
BP001Warninga 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
CPP030Errorgenerated 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
CPP034WarningGenerate 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
CPP035Infoa generated C++ folder an earlier sync wrote was removed, because no module compiles it
CPP036Infothe 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
CPP037Errorthe 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
GEN045Warningthe 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
MEDIA006Warningmedia 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.

Written for
Unity developers · Advanced integrators
Last updated