moeru-ai/airi · error · Error
Invalid Godot stage view-state snapshot payload.
Error message
Invalid Godot stage view-state snapshot payload.
What it means
Thrown by parseStageViewSnapshotPayload when a Godot-emitted snapshot fails safeParse against StageViewSnapshotPayloadSchema. The schema requires `state` (full StageViewStateSchema), a `reason` that must be one of 'loaded' | 'remote-patch' | 'local-input' | 'request' | 'shutdown-flush', an optional `avatarBounds`, and a `requestId` — all under a strictObject that rejects unknown or missing keys. It protects the host from malformed snapshots coming over the Godot bridge.
Source
Thrown at packages/stage-shared/src/godot-stage/view-state.ts:143
state: StageViewState
reason: StageViewSnapshotReason
/** Runtime-only avatar bounds. This is not persisted Godot view state. */
avatarBounds?: StageAvatarBoundsPayload
requestId?: string
}
export const StageViewSnapshotPayloadSchema = strictObject({
state: StageViewStateSchema,
reason: picklist(['loaded', 'remote-patch', 'local-input', 'request', 'shutdown-flush']),
avatarBounds: optional(StageAvatarBoundsPayloadSchema),
requestId: requestIdSchema,
})
/** Parses a Godot-emitted view-state snapshot. */
export function parseStageViewSnapshotPayload(payload: unknown): StageViewSnapshotPayload {
const result = safeParse(StageViewSnapshotPayloadSchema, payload)
if (!result.success)
throw new Error('Invalid Godot stage view-state snapshot payload.')
return {
state: result.output.state,
reason: result.output.reason,
...(result.output.avatarBounds ? { avatarBounds: result.output.avatarBounds } : {}),
...(result.output.requestId != null ? { requestId: result.output.requestId } : {}),
}
}
/** Stable machine-readable Godot view-state error code. */
export type StageViewErrorCode
= | 'invalid-payload'
| 'invalid-state-file'
| 'persistence-failed'
| 'storage-root-missing'
| 'view-state-unavailable'
/** Error event emitted by Godot for view-state request, validation, or lifecycle failures. */View on GitHub (pinned to 677329427f)
Solutions
- Log safeParse issues (r.issues includes the exact path, e.g. ['reason']) before or instead of relying on the wrapped message
- Compare the emitted reason strings in the Godot source with the picklist in view-state.ts and rebuild/align both sides
- Add a regression Vitest case that feeds the real captured payload (from a log) through StageViewSnapshotPayloadSchema
- If a new reason is legitimately added on the Godot side, extend the picklist and StageViewSnapshotReason in the same change
Example fix
// before
const snap = parseStageViewSnapshotPayload(event.data) // generic failure
// after
import { safeParse, StageViewSnapshotPayloadSchema } from '../godot-stage/view-state'
const r = safeParse(StageViewSnapshotPayloadSchema, event.data)
if (!r.success) {
console.error('snapshot issues', r.issues) // shows e.g. reason: invalid option
return
}
const snap = parseStageViewSnapshotPayload(event.data) Defensive patterns
Strategy: validation
Validate before calling
import { safeParse, StageViewSnapshotPayloadSchema } from '../godot-stage/view-state'
const checked = safeParse(StageViewSnapshotPayloadSchema, event.data)
if (!checked.success) {
log.error('snapshot rejected', checked.issues)
return
}
const snap = parseStageViewSnapshotPayload(event.data) Type guard
const isStageViewSnapshotPayload = (p: unknown): boolean => safeParse(StageViewSnapshotPayloadSchema, p).success
Try / catch
catch (e) { if (e.message.includes('view-state snapshot payload')) { requestFreshSnapshot(); return } throw e } Prevention
- Extend the reason picklist in the same commit that adds a new Godot reason
- Never forward snapshot JSON as a string; parse at the transport boundary
- Pin the Godot build and stage-shared version in CI so schema drift fails loudly
When it happens
Trigger: Godot emits a snapshot with a new `reason` value not in the picklist, omits `state` or sends a state with an out-of-range camera field, includes an extra key, or the payload is double-stringified JSON.
Common situations: Updating the Godot engine side (engines/stage-tamagotchi-godot) without rebuilding packages/stage-shared, so the two sides disagree on the reason enum or state shape; a Godot script error producing a half-initialized state table; string/number type drift for camera fields between GDScript and TypeScript.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid Godot stage view-state patch payload.
- Invalid Godot stage view-state error payload.
- Invalid Character Card V3.
- Invalid WebSocket event format.
- Invalid AIRI websocket message.
AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18).
Data as JSON: /api/errors/f2c7d96a84379857.
Report an issue: GitHub.