Repository navigation
known issues
Audience: module authors building against Blacksmith, and contributors.
Known defects and limitations in the current release, with workarounds. This is the counterpart to the CHANGELOG: the CHANGELOG records what was fixed; this records what is still broken. When an item is fixed it moves to the CHANGELOG and leaves this list. Each entry carries a short "Fix" pointer for whoever picks it up.
Security-sensitive issues are not listed here; they are handled privately until patched.
-
create()does not throw on a cross-store duplicate id. It checks only the unplaced store (for unplaced creates) or a single scene (for placed creates), so an id that already exists in the other store is not caught. Callpins.exists(id)first — it checks both. -
update(id, { sceneId })anddelete(id, { sceneId })ignoresceneId. Both search unplaced-then-all-scenes regardless, sodeletewith a mismatchedsceneIdstill deletes the pin. (get()andexists()do honorsceneId.) - An unplaced-created pin's tags do not reach the world tag registry until the pin is first updated or placed.
-
Fix: cross-store id check in
create(); threadsceneIdthrough_findPinLocationforupdate/delete; call_addTagsToRegistryin the unplaced-create branch.
On registerHook, combining once with debounceMs means the callback never runs: once removes the callback as soon as the hook fires, and removal clears the pending debounce timer before it elapses. Separately, throttleMs and debounceMs are mutually exclusive — if both are set, throttleMs wins and debounceMs is silently ignored.
-
Workaround: use at most one of
throttleMs/debounceMs, and do not pairdebounceMswithonce. -
Fix: in
manager-hooks.js, reject (or warn on)once+debounceMsandthrottleMs+debounceMs.
sockets.emit() is documented to reject when delivery fails. That holds under SocketLib (it routes to executeAsUser), but the native fallback never inspects game.users — it returns nothing, which the wrapper turns into a resolved true. On a world without SocketLib, emitting to a disconnected userId resolves true and the message goes nowhere; a try/catch never fires.
-
Workaround: do not treat a resolved
emitas proof of delivery. Checksockets.isUsingSocketLib(), or have the receiver acknowledge explicitly. -
Fix: decide the contract — either make the native path check
game.users.get(userId)?.activeand reject (making the transports genuinely uniform), or scope the documented guarantee per-transport.
Registering an event name that is already registered replaces the previous handler and returns true, with nothing logged, and there is no unregister method. Under the native fallback external handlers land in the same map Blacksmith's internals use (ping, pong, updateCSS, syncTimerState, ...), so registering 'ping' silently destroys Blacksmith's latency checker. SocketLib keeps the namespaces separate.
-
Workaround: always prefix event names with your module id (
'my-module.thing', never'thing'). - Fix: namespace external native handlers (or reject a name owned by internals), and warn on overwrite.
A tool's visible (including a visible: () => false function) is honored on Blacksmith's own toolbar but not on Foundry's native token toolbar. A tool with onFoundry: true and visible false still renders on the Foundry side.
-
Workaround: use
onFoundryas the Foundry-side gate; don't rely onvisibleto hide a tool there. -
Fix: have
getFoundryToolbarTools()honortool.visible, or documentvisibleas Blacksmith-toolbar-only.
The base is fixed as of 13.12.2 — BlacksmithWindowBaseV2 now binds one click listener per instance on the window frame and invokes handlers as fn.call(instance, event, target, instance). A consumer that reads the instance from its third argument (or this) is correct with any number of instances open.
A consumer that still resolves the instance from a shared reference is not. MyWindow._ref, a class static, or a module-level let all point at whichever instance rendered last, so with two open, a click in one window acts on the other. _ref is retained as a deprecated compatibility shim and still behaves that way by design; it is not a bug in the base.
This applies equally to BlacksmithToolWindowBaseV2, which extends the same base (window-tool-base.js:27).
-
Workaround: none needed beyond migrating. Change
MyWindow._ref?.doThing()to(event, target, win) => win.doThing(). -
Fix (consumer side): migrate handler bodies to the instance argument. See "
ACTION_HANDLERSand the instance argument" inapi-window.md. Sibling modules known to still read a singleton are tracked inTODO-GLOBAL.md.
blacksmith.CanvasLayer (and window.BlacksmithCanvasLayer) can be null on the first canvas after load, because the assignment runs later than the first canvasReady. It is also gated behind the enableSceneClickBehaviors setting.
-
Workaround: use
await BlacksmithAPI.getCanvasLayer(), which resolves the layer reliably (it falls back to reading it off the canvas). Seeapi-canvas.md. -
Fix: assign the layer at
init(and eagerly if the canvas is already drawn), outside theenableSceneClickBehaviorsbranch.
styles/window-roll-cinematic.css:441 declares animation: cpb-spin 1.5s linear infinite, but no @keyframes cpb-spin is defined anywhere in styles/. An animation naming an undefined keyframe is silently ignored, so the element renders static.
-
Fix: the intended keyframe is almost certainly
cpb-cinematic-spin(styles/window-roll-cinematic.css:365); rename the reference, or definecpb-spin.
scripts/pins-renderer.js:512 sets --pin-size-px on each pin element, but no stylesheet in the module reads it. Pin dimensions come from inline width/height written alongside it at scripts/pins-renderer.js:510-511. Setting or overriding the property from another module changes nothing.
- Workaround: none for consumers; pin size is not currently overridable through CSS.
-
Fix: either have
styles/pins.csssize pins from the property instead of the inline attributes, or drop the property.
templates/cards-common.hbs:1 wraps the header-hiding marker in <span style="visibility: none">. none is not a valid visibility value, so the declaration is dropped and the span is not hidden. The other 18 templates that use this marker write visibility: hidden.
-
Fix: change
nonetohiddenintemplates/cards-common.hbs:1.
- Ai assistance
- Applicationv2 traps
- CSS traps
- dnd5e conditions
- Documentation standard
- Token actor identity
- Campaign
- Canvas
- Chatcards
- Compendiums
- Contextmenu
- Core
- Create journal entry
- Dialog
- Active Effects
- Entity list
- Equiplocations
- Geography
- GM request
- Gmnotes
- Health
- Hookmanager
- Importer
- Inventory
- Menubar
- Notes
- Party
- Pins
- Quantity split
- Requestroll
- Rolls
- Scene config
- Sockets
- Stats
- Tags
- Toast
- Tokens
- Toolbar
- Window
- Worldclock