Published-release documentation · release compatibility
Troubleshooting
ggaction reports an error when stored state does not determine one safe action. It does not silently select the first mark, scale, dataset, or coordinate. Use the cases below to make the missing decision explicit.
A legend needs more margin
Right-side legends use the default position and require available right margin:
const resized = program.editCanvas({
width: 720,
height: 460,
margin: { top: 40, right: 140, bottom: 70, left: 80 }
});
Use the returned resized program for subsequent calls. If guide creation failed, retry it on this branch. For initial construction, set the same margin in createCanvas.
For a horizontal legend, request bottom placement and reserve bottom margin:
program.createGuides({
legend: { position: "bottom" }
});
The library fails instead of overlapping the plot or clipping labels.
A target cannot be inferred
If the action cannot resolve a compatible current mark or one unique eligible mark, pass the intended mark’s ID:
program.encodeColor({
target: "points",
field: "group"
});
Several marks may coexist while the current mark still makes an omitted target
unambiguous. Explicit IDs are useful when you intend to address another owner.
Check the corresponding data, scale.id, or coordinate selector contract:
inference is action-specific, and some actions always require an explicit ID.
A mark is not ready to materialize
Some actions intentionally leave empty graphics until the semantic relationship is complete:
- A direct Cartesian line needs quantitative or temporal x and quantitative y; aggregation is optional and belongs to the chosen line recipe.
- A histogram needs binned x and count/zero-stack y; prefer
encodeHistogram. - A grouped bar needs a discrete category, a quantitative measure, grouping,
and a matching directional offset. The default band width is available without a
separate
encodeBarWidth; call that action to choose another width. - Points need concrete x/y. A default radius is supplied; an explicit radius or size encoding changes it and is not a prerequisite for materialization.
Keep the documented action order or use the atomic action when one is provided.
A scale cannot be shared
The default scale ID is the channel name. Sharing is valid only between compatible consumers of that same channel and policy. Give an independent consumer a different ID:
program.encodeY({
field: "value",
scale: { id: "detailY" }
});
Do not share one ID across x and y, binned and unbinned consumers, or different aggregate/stack policies.
An explicit domain omits data
Explicit ordinal domains define both membership and order. Include every observed category needed by the connected mark:
program.encodeColor({
field: "Origin",
scale: { domain: ["USA", "Europe", "Japan"] }
});
Unknown categories are rejected rather than appended silently. Missing category combinations are different: grouped bars omit unobserved cells by default instead of synthesizing zero values.
createGuides() selects nothing
createGuides() needs at least one supported encoded axis, grid, or legend.
Create the relevant encodings first. A nominal point color encoding can create
a categorical legend; field-driven shape and quantitative size may be combined
with that legend when their resolved scales are compatible.
createData() rejects the dataset
values must be an array of plain row objects:
program.createData({
values: [
{ category: "A", value: 12 },
{ category: "B", value: 18 }
]
});
Dataset IDs are optional when the generated role is unused. The dataset is
immutable after creation: use filterData, a derived-data action, or create a
new program revision rather than mutating the caller-owned array.
Temporal values cannot be parsed
Temporal fields accept finite timestamps, four-digit numeric or string years, and valid date strings. Four-digit values are interpreted as UTC years.
program.encodeX({ field: "year", fieldType: "temporal" });
Use one consistent representation per field. Invalid or mixed ambiguous values fail before a time scale or partial graphic is created.
A filter produces an empty mark
filterData creates a derived dataset and leaves the source unchanged. An empty
result is valid, so confirm the exact field spelling, value type, and predicate:
const filtered = program.filterData({
id: "selectedOrigins",
field: "Origin",
oneOf: ["Japan", "USA"]
});
Then inspect the derived dataset row count and the final graphic item count.
This continuation assumes program has one current source dataset containing
Origin. Pass source when selecting another dataset. An empty derived dataset
is valid; a subsequent chart that needs an automatic domain can still require
explicit bounds or a non-empty input.
Filtering a numeric field with string values—or a date string with a Date
object—does not coerce the comparison silently.
An encoding is incompatible with the mark
Position and appearance channels are not universally interchangeable. For example, point marks support field-driven shape and size, while line paths use stroke width and stroke dash. Ranged intervals require a compatible primary and secondary position pair.
Start with the generated mark/channel matrix, then open the focused family page for accepted field types and ordering rules. The action fails before storing partial state when the combination is unsupported.
A facet or composition cannot share a resource
Composition snapshots complete child programs and never merges their resource namespaces. Facet sharing is narrower: every represented child scale and guide recipe must be concretely compatible.
Use independent facet scales when panels need different policies:
program.facet({
field: "group",
scales: { y: "independent" },
guides: { legend: false }
});
Polar and Parallel sources can be faceted, but their axes stay local to each
panel. Use theta and public r for Polar scale policies, or
parallelDimensions for all Parallel dimension scales. Requesting
guides: { axes: "outer" } for either coordinate family fails before partial
composition state is stored.
Browser Canvas renders nothing
Call the browser entry point with a real Canvas element and wait until the program is complete:
import { render } from "ggaction";
const context = document.querySelector("#chart").getContext("2d");
render(program, context);
If rendering succeeds but the result is blank, inspect graphicSpec.objects
before changing the renderer. The renderer does not infer missing mark
positions, sizes, or paths from semanticSpec.
Node PNG or PDF export is unavailable in the browser
The PNG and PDF adapters are Node-only package entries:
import { renderToPNG } from "ggaction/png";
import { renderToPDF } from "ggaction/pdf";
await renderToPNG(program, { output: "chart.png", pixelRatio: 2 });
await renderToPDF(program, { output: "chart.pdf" });
Use render from ggaction or renderToSVG from ggaction/svg in the browser.
Importing ggaction/png or ggaction/pdf into a browser bundle is unsupported
because they depend on Node output adapters.
Inspect the program
Use these public states to locate the last successful authoring step:
program.semanticSpec; // stored chart meaning
program.graphicSpec; // fully materialized concrete graphics
program.trace; // hierarchical action history
The renderer reads only graphicSpec; it never repairs incomplete semantic
state. See Actions and trace trees and
Semantic and graphical state.
When rendering succeeds but a mark looks empty, verify the final concrete item cardinality instead of treating a PNG or Canvas call as proof that marks were materialized:
const markId = "points";
const graphic = program.graphicSpec.objects[markId];
if (graphic === undefined) {
throw new Error(`No concrete graphic exists for mark "${markId}".`);
}
const items = program.graphicSpec.objects[markId].items;
if (items.length === 0) {
throw new Error(`Mark "${markId}" materialized no final items.`);
}
Point and rule items usually correspond to final rows, bars correspond to
final bin/category cells, and line or area items correspond to complete series.
Use program.semanticSpec.layers.find(layer => layer.id === markId) to inspect
the owning semantic layer. The map is named graphicSpec.objects, not
graphicSpec.graphics.
Documentation provenance
293 actions · sha256:e755591a2e5f5e2b494867583876e28020a478c5220b77bdc6ad469a6224891d