Skip to main content
hyperframes lint, hyperframes check and the @hyperframes/lint package report findings with a stable code. This page lists every code with its severity, what it catches, and how to fix it. The codes come straight from the rule set in packages/lint/src/rules/ plus the project-level probes that run as part of a project lint. Severities:
  • Error — the composition is broken or the render will fail or visibly diverge. Fails hyperframes check by default.
  • Warning — a quality, determinism or maintainability hazard. Does not fail hyperframes check unless you pass --strict; in render, warnings abort only under --strict-all.
  • Info — a heads-up that needs no action. Shown by hyperframes lint --verbose.
Looking for a code from a render or check log? Codes appear in the output of npx hyperframes lint [dir] — add --json to script over them. check additionally runs runtime, layout and motion audits beyond these lint rules; see Troubleshooting.

Structure and timeline

Source: packages/lint/src/rules/structure.ts. The four codes marked Warning or error are warnings when the CLI lints a project, and errors when Studio lints the file it is editing.

nested_structure_needs_subcomposition

Warning or error. A top-level timeline element contains another nested structure instead of a sub-composition mount. The timeline draws one row per top-level element, and the root composition is built only from sub-compositions. Fix: Move the element and its contents into a sub-composition file and mount it with data-composition-src.

timeline_element_missing_timing

Warning or error. A timeline element has no data-duration, so the timeline cannot draw where it ends. Fix: Add data-duration (in seconds) to the element.

caption_track_kind_missing

Warning or error. An element looks like a caption row but is not marked as one, so the timeline cannot group it on the caption track. Fix: Add data-track-kind="captions" to the element.

multiple_caption_tracks

Warning or error. Captions are spread across several timeline tracks instead of one caption lane. Fix: Give every caption element the same data-track-index.

clip_ends_past_root_duration

Warning. A clip runs past the root composition’s data-duration, so it is cut off in previews, posters and renders. Fix: Extend the root data-duration, or make the clip end at or before it.

Composition contract

Source: packages/lint/src/rules/composition.ts.

invalid_composition_variables_declaration

Error. data-composition-variables is not valid JSON or not an array of variable declarations. Fix: Provide a JSON array such as data-composition-variables='[{"id":"title","type":"string","label":"Title","default":"Hello"}]' — single-quote the attribute, double-quote the JSON.

invalid_variable_values_json

Error. data-variable-values is not valid JSON. Fix: Single-quote the attribute and double-quote the JSON keys and values, e.g. data-variable-values='{"title":"Hello"}'.

unknown_variable_binding

Warning. An element binds a variable that is not declared in data-composition-variables, so the binding silently keeps the authored fallback. Fix: Declare the variable on the composition root (<html>, or the data-composition-id root of a template or fragment composition).

unloadable_media_variable_default

Error. A variable’s default is a URL the runtime refuses to load, so bound elements render their authored fallback src while the render still exits 0. Fix: Use a relative path, http(s), blob:, or a data:image/* URI. Copy the file into the project and reference it relatively.

deprecated_data_end

Error. A legacy data-end is used without data-duration, or a stale data-end disagrees with the data-duration next to it. Fix: Replace data-end with data-duration. The compiler generates data-end from data-duration automatically.

deprecated_data_layer

Error. The legacy data-layer attribute is used. Fix: Replace it with data-track-index, the canonical name Studio and the linter read.

duplicate_composition_id

Error. The same data-composition-id appears on several elements in one file — often a <meta> tag carrying the same id as the root <div>. Fix: Keep data-composition-id on exactly one element: the composition root. Remove it from metadata or duplicate hosts.

html_dir_attribute_breaks_render

Error. <html dir="rtl"> or <html dir="auto"> renders correctly in preview and snapshot but produces a fully blank or black video from render — a confirmed, silent failure. dir="ltr" is not flagged. Fix: Remove dir from <html>, keep lang, and scope the direction to individual text-containing elements instead — direction: rtl in CSS, or dir="auto" on the element.

invalid_parent_traversal_in_asset_path

Error. Asset paths use ../ to traverse above the project root; render and preview clamp or misresolve them. Fix: Point the path at a file inside the project — from compositions/scene.html, both ../assets/x.png and assets/x.png reach the project’s assets folder.

composition_exceeds_inspection_viewport_cap

Warning. The composition is larger than the 4096px viewport cap that check, validate, snapshot, compare and layout audits clamp to. render is unaffected, but a clean inspection result no longer covers the whole canvas. Fix: Keep the authored size if the output needs it, and verify the region past the cap from a render rather than from inspection commands.

composition_file_too_large

Warning. The HTML composition file has grown past a size where it stays easy to read, iterate on, and diff. Fix: Split it into sub-compositions and mount them from the parent with data-composition-src.

composition_heavy_overlay_count_high

Warning. Many elements carry heavy overlay CSS (filter: blur, radial-gradient, clip-path), which can cap capture performance — including hidden ones. Fix: Split the composition and concat the pieces so each capture sees a small subset of heavy overlays; remove overlays that are not visible anyway.

missing_data_no_timeline

Warning. A data-composition-id element (or the root) has neither a matching window.__timelines registration nor data-no-timeline, so the producer polls up to 45 seconds before timing out. Fix: Register the timeline, mount the composition with data-composition-src, or add data-no-timeline for genuinely static sections.

negative_z_index

Warning. z-index below 0 paints the element behind its nearest stacking context’s content; with no stacking-context ancestor that is the composition root, so an opaque background there hides the element entirely. Fix: Give the element’s parent a stacking context (isolation: isolate is the cheapest; transform, filter, opacity below 1, contain: paint, will-change also work).

requestanimationframe_in_composition

Error. requestAnimationFrame runs on wall-clock time, not the GSAP timeline, so it desyncs from frame capture and can flicker or drop frames in renders. Fix: Use GSAP tweens or timeline onUpdate callbacks for animation logic instead.

root_composition_duration_derived

Warning. The root has no data-duration and no GSAP timeline, so its length is inferred from its clips. Fix: Add data-duration="<seconds>" to the root element to set the length yourself.

root_composition_missing_duration_source

Error. The root has no data-duration, no GSAP timeline, and no CSS, WAAPI, Lottie or Three.js animation to infer a duration from, so the render engine cannot determine how long to capture and fails. Fix: Add data-duration="<seconds>" to the root, or add a paused GSAP timeline registered on window.__timelines.

root_composition_missing_html_wrapper

Error. An index.html carrying data-composition-id is a bare fragment without <!DOCTYPE html>, <html> or <body>, so browsers quirks-mode it and the preview server mis-serves it. Fix: Wrap the composition in <!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>...</body></html>.

root_zoom_rescales_a_fixed_canvas

Error. CSS zoom on the canvas rescales the painted composition inside a capture frame that does not rescale with it — the frame is sized from the root’s data-width and data-height in layout pixels. Fix: Author at the real size: set data-width and data-height (and the root’s width and height) and drop the canvas-level zoom. To scale the output without changing layout, pass --output-resolution to render. zoom on descendants is fine.

split_data_attribute_selector

Error. A CSS selector combines two attributes inside one [...] block, which browsers reject — GSAP timelines or querySelector calls fail before they register. Fix: Use separate attribute selectors: [data-composition-id="x"][data-track-index="1"].

standalone_composition_wrapped_in_template

Error. The root index.html is wrapped in a <template> tag, so the runtime cannot play it — only sub-compositions loaded via data-composition-src use <template> wrappers. Fix: Remove the <template> wrapper and serve the composition as a real document.

subcomposition_blanks_before_host

Warning. A mounted sub-composition is blank for part of its host’s window because its own data-duration is shorter than the slot it fills. Fix: Set the sub-composition’s data-duration to fill the host window, or cover the gap with another clip.

subcomposition_root_styled_by_class

Error. A sub-composition’s root element is styled through class selectors, but at render every rule is scoped to [data-composition-id=...], so the class-keyed styles stop applying. Fix: Give the root id="root" and style it with #root plus plain descendant selectors.

template_literal_selector

Error. querySelector is called with a template-literal variable, which the bundler’s CSS parser crashes on. Fix: Use hardcoded string selectors instead of interpolated ones.

timed_element_missing_clip_class

Warning. An element has timing attributes but no class="clip". The runtime still times it, but Studio and the clip-ownership rules use .clip to recognise it, so it is harder to edit and to lint. Fix: Add class="clip" to the element.

timeline_track_too_dense

Warning. One track in this file carries more than 3 timed elements (audio, video, caption cues and sub-composition mounts are not counted), which makes the timeline harder to read, iterate on, and diff. Fix: Split the elements into sub-compositions and mount them from the parent with data-composition-src.

Document, CSS and scripts

Source: packages/lint/src/rules/core.ts.

host_missing_composition_id

Error. A data-composition-src host element has no data-composition-id of its own. Fix: Set data-composition-id on every data-composition-src host element.

css_parse_error

Error. A <style> block fails to parse as CSS. Fix: Fix the reported CSS syntax error.

css_transition_used

Warning. A CSS transition declaration runs on the browser clock and cannot be seeked deterministically across render workers. transition: none and all-zero durations are allowed. Fix: Keep the class or attribute swap for state, but put the visual change on the paused GSAP timeline.

id_override_reduced_specificity

Warning. A selector sets position properties through [id=...] or :where(#id), which has class-level or zero specificity, so a compound class rule on the same element can silently win. Fix: Use the plain #id selector; id specificity beats any selector built only from classes.

id_requires_css_escape

Error / warning. An id starts with a digit, so #id selectors throw a SyntaxError in querySelector and GSAP, and CSS drops the rule. Error when such a selector is actually used in the composition, warning otherwise. Fix: Rename the id to start with a letter (recommended), or build selectors with CSS.escape(id) at runtime.

invalid_inline_script_syntax

Error. An inline <script> is closed with malformed syntax. Fix: Close inline scripts with a valid </script> tag.

missing_timeline_registry

Error. The composition registers no window.__timelines registry at all. Fix: Register each composition timeline on window.__timelines[compositionId].

non_deterministic_code

Error. A script uses a non-deterministic value source (Math.random, Date.now, gsap.utils.random, "random(...)"), so renders can differ between frames or runs. Fix: Precompute the value once at build time and pass a constant, or seed and freeze it.

repeated_id_descendant_selector

Error. A selector requires an id to be nested inside another element with the same id. Ids must be unique, so the selector can never match. Fix: Remove the duplicate ancestor: change #x #x to #x.

root_dimensions_mismatch

Warning. The root’s data-width/data-height were edited directly, but the scaffold’s html, body CSS and <meta viewport> still hold the old resolution, and a stale overflow: hidden body visually clips the correctly-sized root. Fix: Update the body CSS and viewport to match, or scaffold with hyperframes init --resolution portrait.

root_missing_composition_id

Error. The root composition is missing data-composition-id. Fix: Add a stable data-composition-id to the entry composition wrapper.

root_missing_dimensions

Error. The root composition is missing data-width or data-height. Fix: Set numeric data-width and data-height on the entry composition root.

runtime_hidden_style_opacity

Error. A selector observes the runtime-owned hidden style (used to hide native video during frame copying) and forces opacity to zero, which breaks the video replacement pipeline. Fix: Restrict the guard to sub-composition hosts (for example [data-composition-src][style*="visibility: hidden"]) and do not derive element or media opacity from the runtime’s hidden style.

scoped_css_missing_wrapper

Warning. Scoped CSS targets a composition id for which no matching wrapper exists in this HTML. Fix: Keep the matching composition wrapper, or align the CSS scope to an existing wrapper.

studio_missing_editable_id

Warning. A timeline-visible element has no id, so Studio cannot target it stably for timeline and canvas edits. Fix: Add a stable, human-readable id such as id="hero-title" to elements that agents or Studio should edit.

timeline_id_mismatch

Error. A timeline is registered under a key for which no element carries the matching data-composition-id, so the runtime cannot auto-nest it. Fix: Make window.__timelines["<key>"] match the element’s data-composition-id, or vice versa.

unbalanced_style_tags

Error. A <style> block is never closed (following markup is parsed as CSS and disappears), or an extra </style> closes the stylesheet early and dumps CSS into the body as visible text. Fix: Keep <style> and </style> paired.

unclosed_tag_swallowed_element

Error. A tag is missing its closing > before the next <, so the following element is swallowed as bogus attribute text and never becomes a real node. Fix: Close the previous tag’s > before opening the next element.

visible_markup_comment

Error. CSS or JS block-comment syntax (/* ... */) appears in visible HTML markup, where only <!-- ... --> is a comment — so it renders as on-screen text. Fix: Remove the text or convert it to a real HTML comment; keep CSS comments inside <style> and JS comments inside <script>.

GSAP animation

Source: packages/lint/src/rules/gsap.ts.

audio_reactive_single_tween_per_group

Warning. Audio-reactive captions use a single tween per group based on peak values, which sets one static value and is not perceptible as audio reactivity. Fix: Sample the audio data at 100-200ms intervals across each group’s lifetime and create a tween per sample point.

gsap_animates_clip_element

Error. GSAP sets visibility-affecting properties (opacity, visibility, autoAlpha) directly on a clip element whose visibility the framework owns. Fix: Remove the visibility or autoAlpha tween, or move the content into a child <div> and target that instead.

gsap_callback_dom_measurement

Warning. A timeline callback reaches DOM measurement (getBoundingClientRect, getTotalLength, getComputedStyle, …). Callbacks re-fire on every seek, and a cold render worker runs them against a different layout than the sequential pass. Fix: Do all measurement and DOM setup synchronously at build time, before registering the timeline, or derive geometry from fixed composition coordinates.

gsap_cold_seek_hidden_fromto_missing_reveal

Error. An element authored hidden uses gsap.fromTo() whose destination vars omit opacity/autoAlpha, so cold render workers restore the hidden state and the element can stay invisible. Fix: Add opacity: 1 (or autoAlpha: 1) to the destination vars so every seek path establishes the visible end state.

gsap_css_transform_conflict

Error. An element has a CSS transform (for example translateX(-50%) centering) and a GSAP tween animates transform properties — GSAP overwrites the full CSS transform. Fix: Remove the transform from CSS and express it in GSAP properties instead; tl.fromTo with explicit start values is exempt.

gsap_from_opacity_noop

Error. An element has CSS opacity: 0 and a gsap.from() that also sets opacity to 0, so it animates from 0 to 0 and never appears. Fix: Remove opacity: 0 from the CSS or inline style and let the gsap.from() own the initial hidden state.

gsap_fullscreen_overlay_starts_visible

Error. A full-frame overlay starts visible before its first GSAP opacity tween, so it covers earlier render frames — often as a blank or white video. Fix: Author the overlay hidden in CSS, or add an immediate gsap.set(..., { opacity: 0 }) before the reveal tween.

gsap_function_value_hazard

Error / warning. A function-valued tween var reads transform-sensitive geometry, measures layout at tween init, or accesses its first parameter (which is a number, not the element). Error for transform-sensitive reads and bad member access, warning otherwise. Fix: Compute the value once at build time and pass a constant; for per-element logic use the second parameter: (index, target) => ....

gsap_group_selector_keyframes

Warning. A GSAP tween targets several elements with shared keyframes, so editing one element’s keyframes in Studio changes all of them. Fix: Replace the group selector with individual tl.to() calls per element.

gsap_infinite_repeat

Error / warning. A GSAP tween or timeline uses repeat: -1. Warning when the composition declares a finite data-duration (seeking and export are clipped to it), error when nothing bounds the timeline. Fix: Declare a finite data-duration, or replace repeat: -1 with a finite count such as repeat: Math.max(0, Math.floor(totalDuration / cycleDuration) - 1).

gsap_non_transform_motion

Error. A GSAP tween animates layout properties (left, top, width, …), text-reflow properties (fontSize, letterSpacing, …) or sets roundProps, all of which snap to integer device pixels and stutter under frame capture. Fix: Animate transforms instead: positional properties become x/y/scale; reflow properties need per-character splits or a static final value; remove roundProps.

gsap_relative_value_second_writer

Error. A relative tween value (+=...) starts while another writer is still active on the same property, so different seek paths init the relative tween from different bases and the frame snaps. Fix: Use absolute values or a fromTo() with explicit endpoints for that property. A relative value with a single writer is safe.

gsap_repeat_ceil_overshoot

Warning. A repeat count computed with Math.ceil can overshoot the composition duration, extending the animation past it. Fix: Use Math.floor in the repeat calculation: repeat: Math.max(0, Math.floor(totalDuration / cycleDuration) - 1).

gsap_repeat_floor_unclamped

Warning. A repeat calculation can evaluate to -1 when the composition is shorter than one cycle, which GSAP interprets as an infinite repeat. Fix: Clamp the count at zero: repeat: Math.max(0, Math.floor(totalDuration / cycleDuration) - 1).

gsap_repeat_refresh_relative_value

Error. repeatRefresh: true combined with a relative value accumulates per iteration; a cold render worker seeking into iteration N never performed the earlier accumulation, so its result differs. Fix: Remove repeatRefresh: true, or replace the relative value with absolute endpoints (a fromTo()).

gsap_repeated_fromto_without_baseline

Warning. Several tl.fromTo() calls target the same element with no stable baseline, so the last-authored “from” values become the resting state for any seek before the first tween runs. Fix: Add immediateRender: false to every fromTo except the earliest-positioned one.

gsap_timeline_not_registered

Error. A GSAP timeline is created but never registered on window.__timelines, so the runtime never discovers it and the animation does not play in preview or render. Fix: Add window.__timelines = window.__timelines || {}; and assign the timeline under the composition’s data-composition-id.

gsap_timeline_registered_before_async_build

Error. window.__timelines is assigned before the timeline is built inside document.fonts.ready, so an empty timeline registers first and can nest empty in sub-compositions. Fix: Move the window.__timelines[id] = tl assignment to the end of the async build callback, after the tweens are added.

gsap_timeline_return_used_as_tween

Error. Code captures the return of tl.to()/tl.from()/tl.set() expecting the tween, but those methods return the timeline itself — so a later kill() or overwrite hits the whole timeline. Fix: Put the group of tweens in a nested timeline (gsap.timeline() added with tl.add(nested, 0)) and kill that instead.

gsap_timeline_set_initial_hide

Warning. The initial hidden state is set with tl.set(..., 0) inside the paused timeline; a zero-duration set at position 0 does not render while the playhead sits exactly at 0, so frame 0 shows the un-hidden state. Fix: Use gsap.set(...) outside the timeline, or author the hidden state directly in CSS or attributes.

gsap_undefined_css_variable

Warning. GSAP animates a var(--...) CSS variable that no static CSS or composition-variable declaration defines, so the computed color may become invalid or transparent. Fix: Define the variable in CSS, declare it in data-composition-variables, or add a var(--x, #fff) fallback.

missing_gsap_plugin

Error. A tween uses a plugin-specific property such as motionPath, but the plugin is not loaded and registered, so core GSAP ignores the property. Fix: Load the plugin before the animation script and register it with gsap.registerPlugin(...), or express the motion with core x/y tweens.

missing_gsap_script

Error. The composition uses GSAP but no GSAP script is loaded, so the animation will not run. Fix: Add the GSAP <script> tag before the animation script.

overlapping_gsap_tweens

Warning. Two GSAP tweens overlap on the same target for the same properties, so the later one overwrites the earlier mid-flight. Fix: Shorten the earlier tween, move the later one, or add overwrite: "auto".

svg_drawon_css_dasharray_conflict

Error. GSAP writes strokeDasharray on an element whose CSS declares a multi-component stroke-dasharray; GSAP merges dash lists per component, so the CSS gap survives and the draw-on breaks. Fix: Remove the CSS stroke-dasharray (decorative dashes belong on a separate element), or set the full two-component value in GSAP.

svg_measure_before_path_d

Error / warning. getTotalLength() is called on a path with no static d attribute, so it returns 0 and the dash animation dies. Error when no d assignment exists anywhere, warning when d is only assigned inside a function body. Fix: Assign the path’s d synchronously at build time (top level, before measuring), or author a static d attribute in the HTML.

Media elements

Source: packages/lint/src/rules/media.ts.

audio_carve_ungrouped_sources

Warning. A voiceover carve names its member clips directly instead of carving an <hf-audio-group>, so the list silently rots when clips change. Fix: Group the voice clips and carve against the group.

audio_group_carve_attr

Warning. An <hf-audio-group> carries data-fx-carve (or a carve-derived chain), but a carve belongs on the bed clip, not the bus. Fix: Remove data-fx-carve from the group and carve the bed clip instead.

audio_group_no_members

Error. An audio group exists that no clip joins, so its fader, effects and automation are silently dropped. Fix: Add data-audio-group="<id>" to the clips the bus is for, or delete the bus.

audio_group_timing_attrs

Warning. An <hf-audio-group> carries timing attributes (data-start, data-duration, data-track-index). A group has no clip window of its own; its members carry the timing. Fix: Remove the timing attributes from the group and set them on the member clips.

audio_volume_double_automation

Warning. An element has both a volume automation lane and a GSAP tween on volume; the lane wins and the tween is ignored in preview and render. Fix: Keep one: delete the lane to tween, or drop the tween and shape the level in the lane.

audio_volume_tween_overrides_gain

Warning. An element has data-volume gain and a GSAP tween on volume; tween values are absolute and replace the gain rather than scale it. Fix: Write tween targets as absolute gains that already include the gain, or reset data-volume to 1 and let the tween carry the level.

base64_media_prohibited

Error. Inline base64 audio or video is present. Base64 media bloats file size and breaks rendering. Fix: Use a relative path or HTTPS URL for the src — never embed media as base64.

color_grading_invalid_json

Error. data-color-grading on a <video> or <img> starts with { or [ but is not valid JSON, so the grade does not render. Fix: Use valid JSON, for example data-color-grading='{"preset":"skin-soft","intensity":0.6,"adjust":{"highlights":-0.08}}' — single-quote the attribute, double-quote the JSON.

color_grading_invalid_structure

Error. data-color-grading parses as JSON but breaks the grading contract — an unsupported key, a value of the wrong type, or a number out of range. The runtime silently ignores unknown keys, so a control can look applied in the HTML yet never render. One finding is reported per problem, naming the offending path. Fix: Correct or remove the value the finding names, following the Color grading reference.

color_grading_non_media

Error. data-color-grading is set on an element other than <video> or <img>, such as a wrapper or an element with a CSS background. The shader runtime only grades real <video> and <img> elements, so the attribute has no effect. Fix: Move the attribute to the <video> or <img> element itself.

duplicate_media_discovery_risk

Warning. Several matching media entries share the same source, start and duration, so the compiler can discover the same media twice. Fix: Remove the duplicated media nodes so discovery sees one.

duplicate_media_id

Error. A media id is defined on multiple elements, so preview and the producer can build different media graphs. Fix: Give each media element a unique id.

imperative_media_control

Error. An inline script controls managed media imperatively (play, pause, currentTime, …). HyperFrames must own playback to keep preview, timeline and renders deterministic. Fix: Express timing with data-start/data-duration and media offsets such as data-media-start instead.

media_crossorigin_breaks_preview

Error. The element has crossorigin, which forces a CORS-checked fetch; if the host omits CORS headers, the load fails silently in Studio preview (blank media) while renders still work. Fix: Remove crossorigin unless you read the media back through canvas, WebGL or WebAudio from a host known to send CORS headers.

media_missing_data_start

Error. A media element has src but no data-start, so HyperFrames cannot own its playback and preview and render can diverge. Fix: Add data-start="0" (or the intended start) and data-duration if the clip should stop before the source ends.

media_missing_id

Error. A timed media element has no id, so the renderer cannot discover it — the audio goes silent or the video freezes. Fix: Add a unique id attribute to the element.

media_missing_src

Error. A timed media element has no src (on the element or a <source> child), so the renderer cannot load it. Fix: Add src to the element, or a <source src="..."> child.

media_preload_none

Warning. The element has preload="none", which prevents the renderer from loading the media (the compiler strips it for renders, but preview can misbehave). Fix: Remove preload="none" or change it to preload="auto".

media_runtime_src_mutation

Warning. An inline script mutates the src of existing managed media, which races media discovery and extraction. Fix: Author the final static src, or bind data-var-src to a declared variable so the source is applied before discovery.

media_src_kind_mismatch

Error. A media URL’s kind does not match its element — for example a still image on <video> or a video URL on <img>, so the element renders nothing. Fix: Match the element to the media: <img> for stills, <video> for video URLs, <audio> for sound.

media_variable_src_no_fallback

Warning. The element relies on data-var-src with no fallback src, so renders without that variable value cannot load the media, and audio extraction reads the authored src. Fix: Add a fallback src the composition can render with when the variable is not provided.

nested_media_start_basis_ambiguous

Warning. A nested media element’s data-start is ambiguous: composition-local or a legacy root-global timestamp, and the two read as different frames. Fix: Keep data-start composition-local, or mark the legacy basis with data-hf-media-start-basis="global"; otherwise convert to local time.

placeholder_media_url

Error. The element uses a placeholder media URL that will 404 at render time. Fix: Replace it with a real, reachable media URL.

self_closing_media_tag

Error. A self-closing media tag (<video ... />) is invalid HTML: the browser leaves the tag open and swallows all subsequent elements as invisible fallback content, making the composition invisible. Fix: Write explicit closing tags: <video ...></video>. Media elements must never be self-closed.

speed_ramp_on_non_media

Warning. A speed-ramp lane sits on a non-media element; only <video> and <audio> clips can be retimed, so the lane does nothing. Fix: Move the rate lane onto the video or audio clip, or retime an animation with a GSAP timeline.

video_audio_double_source

Error. An <audio> element and a <video> element point at the same source at the same time; the unmuted video already carries the sound, so playback echoes. Fix: Let the video carry its own sound (recommended), or mute the video and keep the separate <audio> when picture and sound must be cut independently.

video_missing_muted

Error. A timed <video> declares neither muted nor data-has-audio="true", so the linter cannot tell silent footage from sound-carrying footage. Fix: Add data-has-audio="true" when the file has sound, or muted when it is silent.

video_muted_with_declared_audio

Error. A <video> declares data-has-audio="true" but also carries muted, so Studio preview silences the audio the declaration promises. Fix: Remove muted if the video should be audible, or drop data-has-audio and use data-volume="0" for silent visual video.

video_nested_in_timed_element

Error. A <video> with data-start is nested inside another timed element; the frame extractor resolves the video’s start without the wrapper’s offset while visibility uses the wrapper’s window, so the clip shows wrong frames and disappears partway. Fix: Time the wrapper or the video, never both: remove data-start from the wrapper, or move the <video> up to be a direct child of the stage.

Captions

Source: packages/lint/src/rules/captions.ts.

caption_container_relative_position

Error. A caption selector uses position: relative, which causes overflow and breaks caption stacking. Fix: Use position: absolute for all caption elements.

caption_exit_missing_hard_kill

Error. Caption exit animations (tl.to with opacity: 0) have no hard tl.set kill, so conflicting karaoke tweens can leave captions stuck on screen. Fix: Add tl.set(groupEl, { opacity: 0, visibility: "hidden" }, group.end) after every exit tween as a deterministic kill.

caption_fittext_scale_mismatch

Warning. fitTextFontSize uses a maxWidth that leaves no headroom for emphasis words scaled above 1x, so the effective width can overflow the composition. Fix: Reduce maxWidth to leave headroom for the scale factor.

caption_overflow_clips_scaled_words

Error. A caption container has overflow: hidden while GSAP scales caption words above 1x, so scaled emphasis words and their glow are clipped. Fix: Use overflow: visible on caption containers and prevent overflow with fitTextFontSize and a reduced maxWidth instead.

caption_text_overflow_risk

Warning. A caption selector has white-space: nowrap but no max-width, so long phrases clip off-screen. Fix: Add max-width: 1600px (landscape) or 900px (portrait), keeping overflow visible.

caption_textshadow_on_group_container

Warning. textShadow is tweened on a caption group container; with semi-transparent children the glow renders as a visible rectangle behind the whole group. Fix: Apply textShadow to individual active word elements and use scale on the group for pulsing.

caption_transcript_not_inline

Error. A captions composition loads its transcript with fetch(), but the Studio caption editor needs an inline var TRANSCRIPT = [...] array to detect and edit captions. Fix: Embed the transcript inline as var TRANSCRIPT = [...] with JSON-quoted keys.

Fonts

Source: packages/lint/src/rules/fonts.ts.

font_family_without_font_face

Error. A @font-face-undeclared font family is used, so the renderer cannot supply it and text falls back to a default face. Fix: Declare each family with @font-face pointing at the captured .woff2 files, or bundle the font under capture/assets/fonts/.

system_font_will_alias

Warning. A system font family is used without a matching @font-face; in distributed rendering system-font capture is disabled, so the font is substituted at render time and text can reflow. Only reported when the linter runs with the distributed: true option (linting for distributed or Lambda renders); a local render captures the system font as designed, so it is not flagged there. Fix: Bundle the font with @font-face and a project-local file instead of relying on OS fonts.

Textures

Source: packages/lint/src/rules/textures.ts.

texture_class_missing_base

Warning. A texture material class is used without the base hf-texture-text class, so the texture pipeline does not engage. Fix: Add the base class alongside the material class, e.g. class="hf-texture-text hf-texture-lava".

texture_class_unknown

Error. A texture material class is not defined by any local CSS, so the text renders untextured. Fix: Paste the Texture Mask Text component’s <style> block into the composition, or fix the class typo.

texture_drop_shadow_on_text

Warning. A drop shadow is applied directly to textured text, which shadows the mask rather than the glyphs. Fix: Wrap the textured text and apply filter: drop-shadow(...) to the wrapper, not the hf-texture-text element.

texture_text_missing_mask

Warning. hf-texture-text is used without a texture material class or a custom mask-image, so there is nothing to reveal. Fix: Add a material class such as hf-texture-lava, or set mask-image and -webkit-mask-image on the element.

Slideshows

Source: packages/lint/src/rules/slideshow.ts.

slideshow_invalid

Error. A slideshow island contains invalid JSON or a structure that does not match the manifest schema. Fix: Make the <script type="application/hyperframes-slideshow+json"> block valid JSON matching the SlideshowManifest schema.

slideshow_unresolved_ref

Error. The slideshow manifest references a sceneId with no matching scene element, or a scene with no explicit window. Fix: Make every sceneId match a scene element’s data-composition-id, or provide explicit startTime/endTime.

Adapters: Lottie and Three.js

Source: packages/lint/src/rules/adapters.ts.

missing_lottie_script

Error. The composition uses Lottie but no Lottie script is loaded, so the animation will not render. Fix: Add the lottie-web <script> tag before the Lottie code.

missing_three_script

Error. The composition uses Three.js but no Three.js script is loaded, so the 3D scene will not render. Fix: Add the Three.js <script> tag before the Three.js code.

Linked clips

Source: packages/lint/src/rules/linkedClips.ts.

linked_clip_orphan

Warning. A clip carries data-link whose partner clip no longer exists, so it edits as a group of one. Fix: Remove the data-link attribute, or restore the partner clip.

linked_clips_out_of_sync

Warning. Clips sharing a data-link differ in their timing attributes, but linked clips are edited as one. Fix: Give every member the same data-start, data-duration, data-media-start and data-playback-rate, or remove data-link from all of them to unlink.

Project-level checks

Source: the project lint pass in packages/lint/src/project.ts, packages/lint/src/hyperframeLinter.ts and packages/lint/src/hevcPreviewLint.ts.

multiple_root_compositions

Error. Several root-level HTML files carry data-composition-id, so the runtime may discover multiple entry points and duplicate audio playback. Fix: Keep exactly one root index.html with data-composition-id; remove or rename the extra files.

blank_root_with_standalone_composition

Error. The default index.html has no renderable content while another file holds a standalone timed composition, so default commands open an empty video. Fix: Move the composition into index.html, or mount it with data-composition-src; if the separate file is intentional, render it explicitly.

missing_or_empty_sub_composition

Error. A data-composition-src points at a folder, an empty file, or a missing file, so the mount is unusable and the render pre-flight rejects it. Fix: Point data-composition-src at the HTML file (such as <folder>/index.html) and make sure it renders content.

missing_local_asset

Error. An element references local files that do not exist in the project, so the renderer silently skips them and the video ships with missing visuals. Fix: Add the file to the project directory, or update the src to an existing file. A common cause is renamed captured assets.

audio_file_without_element

Warning. The project contains audio files but no <audio> element in any composition, so the rendered video will be silent. Fix: Add an <audio> element referencing the file.

audio_src_not_found

Error. An <audio> element references files that are not in the project, so the rendered video will be silent. Fix: Add the missing file(s) to the project directory, or update the src to point at existing files.

duplicate_audio_track

Warning. Two audible elements overlap on the same track index, so their audio doubles up. Fix: Use non-overlapping time windows or different track indices.

texture_mask_asset_not_found

Error. A CSS mask-image references files that are not in the project, so the texture mask fails to load. Fix: Add the missing mask file(s) to the project, or update the mask-image URL.

inaccessible_media_url

Error. A remote media URL did not answer a HEAD request with a 2xx status (or the request failed or timed out), so the media cannot load. Reported only by the async lintMediaUrls() export of @hyperframes/lint; hyperframes lint and hyperframes check do not run it. Fix: Replace it with a valid, reachable media URL.

hevc_preview_codec

Info. A video file uses the HEVC/H.265 codec. Renders are unaffected (FFmpeg pre-decodes), and preview automatically falls back to a cached H.264 proxy when the browser cannot decode HEVC. Fix: Nothing required. If preview playback still fails, run hyperframes doctor and check that media auto-proxying is enabled.

video_media_start_at_or_past_eof

Warning. A video’s data-media-start sits at or past the source’s playable duration, so the video holds its final frame for the whole slot. Fix: Trim data-media-start below the source duration if the final-frame hold is unintended.