All Gotchas
This is the current consolidated list of confirmed HiStruct/FCS gotchas. Each entry includes the symptom, likely cause, and the fix or workaround that actually helps in practice.
1. Interactive reload in console mode does not always pick up changes
Symptom
- You edit an
.fcsfile, press reload in the interactive console, and old behavior remains.
Cause
- The regular interactive console reload path is not always reliable for file refresh.
Fix / workaround
- Prefer
--i-jsonand send{"reload":true}explicitly.
Example
{"reload":true}
2. .fcsconfig.json EngineVersion must be specific
Symptom
- The interpreter or tooling starts with the wrong engine behavior, or the component fails to load as expected.
Cause
- Generic values such as
"e4"are too vague.
Fix / workaround
- Use a specific version such as
"e4.trans".
Example
{
"EngineVersion": "e4.trans"
}
3. fli.exe needs a real TTY for interactive mode
Symptom
- Interactive mode hangs, behaves strangely, or fails in automation.
Cause
fli.exeexpects a true interactive terminal for its console mode.
Fix / workaround
- Use an async-capable shell session or switch to
--i-jsonfor automation.
Example
- Good for agents: JSON REPL mode
- Good for humans: real terminal/TTY
4. --fcs requires an absolute path
Symptom
- The CLI cannot find the script, or the resolved path is doubled incorrectly.
Cause
- Relative paths can trigger a path-doubling bug in
--fcshandling.
Fix / workaround
- Always pass an absolute path to
--fcs.
Example
--fcs D:\Projects\MyComponent\main.fcs
5. Assignment syntax: := vs =
Symptom
- Values do not end up where expected, or object fields are not assigned correctly.
Cause
- Top-level variables and object/parameter assignments use different assignment forms.
Fix / workaround
- Use
:=for top-level variable assignment. - Use
=inside objects and parameter UI item definitions unless the specific construct expects otherwise.
Example
height := 5.0
obj = { Width = 3.0, Height = height }
6. Parentheses are required in geometry expressions
Symptom
- Coordinates fail to parse or geometry expressions produce confusing errors.
Cause
- Inline arithmetic inside geometry coordinates is stricter than it first appears.
Fix / workaround
- Wrap expressions in parentheses.
Example
Origin = {(span/2), 0, (height + offset)}
7. Lazy evaluation means variables reflect the latest value
Symptom
- A variable seems to "change in the past" after a later reassignment.
Cause
- FCS uses lazy evaluation; references can resolve using the latest value at evaluation time.
Fix / workaround
- Be deliberate about when values are derived.
- Avoid reusing the same variable name for conceptually different stages.
Example
x := 1
p := x
x := 2
# p may resolve using the latest x depending on context
8. GClass parameter typos create new variables silently
Symptom
- Child parameters do not update, but there is no obvious error.
Cause
- A misspelled override name becomes a new variable instead of overriding the intended parameter.
Fix / workaround
- Verify parameter names exactly against the child file.
- Test one instance before scaling up.
Example
gclass {gcDoor} filename "door.fcs" parameters { Heigth = 2.1 }
If the child expects Height, this silently fails.
9. File paths in FCS should use forward slashes
Symptom
- A path works in Windows tools but fails or behaves inconsistently inside FCS.
Cause
- Backslashes can be interpreted awkwardly depending on parsing context.
Fix / workaround
- Prefer forward slashes in FCS file paths.
Example
filename "Components/door.fcs"
10. --i-json output may contain non-JSON first lines
Symptom
- A JSON parser fails immediately on startup output.
Cause
- The tool may emit banner or log lines before the first JSON message.
Fix / workaround
- Ignore non-JSON preamble lines until structured JSON starts.
Example
- Do not assume line 1 is valid JSON.
- Filter until the first line beginning with
{or[if your workflow allows that.
11. The agent account may not have palette admin rights
Symptom
- Operations involving palettes, editing, or admin-only actions fail despite apparently correct requests.
Cause
- The authenticated account lacks the necessary role.
Fix / workaround
- Check
UserSessionand confirm the assigned roles before debugging code.
Example
- If the user is not a palette admin, no amount of request tweaking will grant that capability.
12. .fcscdm and .fcscdx files must be UTF-16 encoded
Symptom
- Files look correct in the editor but fail to load, parse, or generate scenes correctly.
Cause
- These file types expect UTF-16 encoding.
Fix / workaround
- Save
.fcscdmand.fcscdxfiles as UTF-16.
Example
- If a generated scene definition keeps failing mysteriously, check encoding before changing content.
13. Booster LocalComponentsFolder resolution is indirect
Symptom
- Booster cannot find a local component even though the folder seems correct.
Cause
DirPathis resolved as a folder name underLocalComponentsFolder, not as an arbitrary absolute path.
Fix / workaround
- Ensure the component folder name under
LocalComponentsFoldermatches the expectedDirPathresolution.
Example
LocalComponentsFolder = C:\HiStruct\Components- component
DirPath = SkyNetRecognizer - expected folder becomes
C:\HiStruct\Components\SkyNetRecognizer
14. Wrong space route key can cause silent 403 or empty results
Symptom
- Calls succeed at the transport level but return 403, empty payloads, or apparently missing data.
Cause
- The route key is wrong, for example using
skynetinstead ofsky.
Fix / workaround
- Verify the actual route key from
UserSessionor the deployed space configuration.
Example
- Do not infer the route key from the display name.
15. Check UserSession before blaming auth code
Symptom
- You spend time debugging tokens, headers, or request logic because of a 403.
Cause
- The user or agent simply does not have the required role assignment.
Fix / workaround
- Inspect
UserSessionfirst, then inspect code.
Example
- Missing role assignment is often the true cause of a "mysterious" 403.
16. Views.fcscdx must include ProjectionSettingsFile
Symptom
- Scene generation crashes or fails unexpectedly.
Cause
- The view definition is incomplete without
ProjectionSettingsFile.
Fix / workaround
- Add
ProjectionSettingsFiletoViews.fcscdx.
Example
- If the scene crashes immediately after view generation starts, inspect the view definition first.
17. Local component versions are file-timestamp based
Symptom
- Updating files does not affect already-created component instances.
Cause
- Local component versioning is tied to file timestamps, and existing instances remain pinned to the version they were created from.
Fix / workaround
- Create a new component instance after relevant file changes.
Example
- Editing the source file alone is not enough when an existing instance still references the old timestamped version.
18. Shift+R refresh requires Admin role
Symptom
- A developer can open the space but cannot perform the expected refresh action.
Cause
SpaceDeveloperis not sufficient for this specific refresh capability.
Fix / workaround
- Use an account with Admin role when you need
Shift+Rrefresh.
Example
- Do not assume space development access automatically implies administrative refresh rights.
19. Booster auto-detects file changes
Symptom
- You restart Booster unnecessarily after every edit.
Cause
- It is easy to assume a restart is required when the watcher already handles updates.
Fix / workaround
- Let Booster detect file changes automatically; restart only when troubleshooting a real watcher problem.
Example
- Edit component files, then validate whether Booster picked them up before bouncing the process.
20. SkiaSharp fails in Engine container — missing native dependencies
Symptom
- Expression evaluation fails with:
"The type initializer for 'SkiaSharp.SKData' threw an exception" → Unable to load shared library 'libSkiaSharp' → libfontconfig.so.1: cannot open shared object file - Happens when evaluating expressions like
Fcs.Presentation.ImageData.FromBase64(maskBase64)or any SkiaSharp-dependent code on the server
Cause
- The
Dockerfile-engineis missing theapt-get installblock for native libraries (fontconfig,libgdiplus,libx11-dev). The comment was left behind but the actualRUNcommand was removed during the .NET 10 migration. - The
Dockerfile-jobs(WebJobs2) has the correct block, so SkiaSharp works there. - Expression evaluation always runs on the Engine container (webapp → HTTP → engine), so the engine needs the native libs.
Fix / workaround
- Fix: Add native deps to
Dockerfile-engine(matchDockerfile-jobs):RUN apt-get update \ && apt-get install -y --allow-unauthenticated \ libc6-dev libgdiplus libx11-dev fontconfig \ && rm -rf /var/lib/apt/lists/* - Workaround: Use non-quick triggers (
IsQuick = False) for constraints that need SkiaSharp — they dispatch to the WebJobs container. But note: this only helps for constraint execution, NOT for lazy FCS expressions.
Example
- The vectorization pipeline (
ImageData.FromBase64→ToBoolMaskByRed→RecognizeBoolMaskAreaToBufferedGeometry) fails on the engine because it's a lazy expression, not a constraint.
21. Quick vs non-quick trigger dispatch affects which container runs your code
Symptom
- A trigger works when set to
IsQuick = Falsebut fails withIsQuick = True(or vice versa) - Background processing takes longer than expected when
IsQuick = Trueshould suffice
Cause
IsQuick = Trueruns constraints inline in the Engine container (fast, synchronous)IsQuick = Falsedispatches to the WebJobs2 container (async, background)- The two containers may have different native dependencies installed
- Even with
IsQuick = False, several conditions can override to quick mode: (1):quicksuffix on trigger name, (2)__QuickTriggernaming convention, (3) user edited values in the VM, (4) non-action ParamValueDto type
Fix / workaround
- Set
IsQuickdeliberately based on where you need the code to run - For API callers: null out
ParamValueDtoand don't include:quickin the trigger name to force WebJob path - See Constraint Execution → Quick vs Non-Quick for full details
Example
- A detection pipeline with
IsQuick = Trueruns in ~3s (engine inline), same pipeline withIsQuick = Falsequeues as WebJob and takes ~8s but has full native library support.
22. Init constraint stores wrong/empty value in multi-step triggers
Symptom
- A constraint's
Initexpression evaluates to{}or an empty/default value, even though preceding constraints should have populated its dependencies.
Cause
- Using
Init = expr(eager) instead ofInit := expr(lazy). Eager evaluation happens at constraint object construction time, before the trigger engine processes preceding steps. - Embedding trigger objects directly (
recognitionTrigger.Constraints) forces evaluation of all nested expressions when the outer trigger object is built.
Fix / workaround
- Always use
Init := expr(lazy) for Init attributes - Use string constraint references (
"myConstraint") instead of direct object embedding — strings are resolved at execution time - Both can be combined for double safety
Example
# BEFORE (broken): Init evaluated eagerly before steps 1-2 run
detectBuildingsTrigger := {
Constraints = [
recognitionTrigger.Constraints,
vectorizeTrigger.Constraints, # Init = maskSmoothGeom → {} because maskBase64 is empty
],
}
# AFTER (correct): string refs + lazy Init
vectorizeConstraint := {
Variable = "buildingOutlines",
Init := maskSmoothGeom.ToPersistenceString(-8), # lazy + string persistence
}
detectBuildingsTrigger := {
Constraints = ["recognitionConstraint", "downloadMaskConstraint", "vectorizeConstraint"],
}
23. Cannot store FcsBufferedGeometry in PVC
Symptom
- Constraint fires successfully but PVC variable remains empty/null.
- Commit response shows: "Can not convert 'FCS.Geometry.Buffering.FcsBufferedGeometry' to ParameterValueClass"
Cause
FcsBufferedGeometryis a live in-memory object, not PVC-serializable. TheConvertFmValueToPvcconverter doesn't know how to turn it into aParameterValue.
Fix / workaround
- Serialize to a persistence string:
geometry.ToPersistenceString(-8) - Store in an
ItemStringparameter (notItemGallery) - Reconstruct in scene with
Fcs.Geometry.Buffered.Parse(stringFromPvc)
Example
buildingOutlines := "" # ItemString in PVC
# In trigger constraint:
Init := maskSmoothGeom.ToPersistenceString(-8)
# In scene:
gblock {gb} gcomposite [Fcs.Geometry.Composite(Fcs.Geometry.Buffered.Parse(buildingOutlines))]
Quick triage checklist
When something breaks, check these first:
- Is the path absolute where required?
- Is the file encoding correct?
- Is the route key correct?
- Does
UserSessionshow the expected roles? - Is the component instance pinned to an older timestamped version?
- Are you using the correct interactive mode for the tool?
- Did you include required view/projection files?
- Does the container have the required native dependencies (SkiaSharp, fontconfig)?