moeru-ai/airi · warning · HttpError
EXTENSION_ASSET_METHOD_NOT_ALLOWED
EXTENSION_ASSET_METHOD_NOT_ALLOWED
Error message
Method Not Allowed
What it means
An HttpError with status 405 thrown by the extension static-asset route handler when the request method is neither GET nor HEAD. The route exists to serve plugin iframe assets (/_airi/extensions/:extensionId/sessions/:assetSessionId/ui/**assetPath) and intentionally rejects mutating verbs before any auth/asset resolution. The stable code EXTENSION_ASSET_METHOD_NOT_ALLOWED lets callers distinguish it from other 405s.
Source
Thrown at apps/stage-tamagotchi/src/main/services/airi/http-server/static-assets/route.ts:50
* Use when:
* - Serving plugin iframe assets under `/_airi/extensions/:extensionId/sessions/:assetSessionId/ui/**assetPath`
*
* Expects:
* - Cookie-backed asset session data to be present and valid
* - `resolveAsset` to map request params into a validated local file
*
* Returns:
* - H3 event handler that enforces cookie auth before static file response
*/
export function createStaticAssetRoute(options: StaticAssetRouteOptions) {
return eventHandler(async (event) => {
try {
Object.entries(staticAssetSecurityHeaders).forEach(([key, value]) => {
event.res.headers.set(key, value)
})
if (event.req.method !== 'GET' && event.req.method !== 'HEAD') {
throw new HttpError({
status: 405,
code: 'EXTENSION_ASSET_METHOD_NOT_ALLOWED',
message: 'Method Not Allowed',
})
}
const requestPath = parseStaticAssetRequestPath(getRequestURL(event).pathname)
const extensionId = requestPath?.extensionId ?? ''
const assetSessionId = requestPath?.assetSessionId ?? ''
const assetPath = normalizeStaticAssetPath(requestPath?.assetPath ?? '')
if (!extensionId || !assetSessionId || !assetPath) {
throw new HttpError({
status: 401,
code: 'EXTENSION_ASSET_REQUEST_INVALID',
message: 'Unauthorized',
reason: 'required extensionId, assetSessionId, or assetPath is missing',
})View on GitHub (pinned to 27111382b4)
Solutions
- Issue only GET (or HEAD) requests to extension static-asset URLs; they serve files, not actions.
- Point mutating plugin API calls at the dedicated extension API route, not the ui/** asset path.
- If you need OPTIONS support for CORS preflight, add an explicit OPTIONS handler rather than relying on the asset route.
- Inspect the request method and URL in the browser network panel to confirm the wrong verb/endpoint combination.
Example fix
// before
fetch(`/_airi/extensions/${extId}/sessions/${sid}/ui/index.html`, { method: 'POST' })
// after
fetch(`/_airi/extensions/${extId}/sessions/${sid}/ui/index.html`, { method: 'GET' }) Defensive patterns
Strategy: validation
Validate before calling
function isAllowedAssetMethod(method: string): boolean {
return method === 'GET' || method === 'HEAD'
}
if (!isAllowedAssetMethod(request.method)) {
// point the client at the extension API route instead
} Type guard
function isAllowedAssetMethod(method: string): boolean {
return method === 'GET' || method === 'HEAD'
} Try / catch
try {
await fetch(assetUrl, { method })
}
catch (error) {
if (error?.code === 'EXTENSION_ASSET_METHOD_NOT_ALLOWED') {
// switch to GET, or route the call to the extension API endpoint
}
} Prevention
- Use only GET/HEAD for extension static-asset URLs.
- Route mutating plugin calls to the dedicated extension API route.
- Add an explicit OPTIONS handler if CORS preflight is needed on the asset path.
When it happens
Trigger: Any POST/PUT/DELETE/PATCH/OPTIONS request hitting the static-asset route URL. Because the check runs after security headers are set but before auth and path parsing, it fires uniformly for all non-GET/HEAD methods regardless of credentials.
Common situations: A plugin iframe host posting a form to an asset URL by mistake; a reverse proxy health check using OPTIONS; a misconfigured client treating the asset endpoint as an upload target; browser preflight (OPTIONS) before a cross-origin request to the asset path.
Related errors
- EXTENSION_ASSET_REQUEST_INVALID
- Failed to apply server channel configuration
- Invalid Godot stage scene input payload.
- Godot stage is not running.
- Godot stage bridge is not connected.
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/a3e28fa72b9537bc.
Report an issue: GitHub.