fix(matrix): treat missing room state as empty

This commit is contained in:
Gustavo Madeira Santana
2026-03-30 22:19:21 -04:00
parent d95f4ef9d2
commit 69b5229632
5 changed files with 49 additions and 19 deletions

View File

@@ -1,3 +1,4 @@
import { isMatrixNotFoundError } from "../errors.js";
import { resolveMatrixMessageAttachment, resolveMatrixMessageBody } from "../media-text.js";
import { fetchMatrixPollMessageSummary } from "../poll-summary.js";
import type { MatrixClient } from "../sdk.js";
@@ -58,10 +59,7 @@ export async function readPinnedEvents(client: MatrixClient, roomId: string): Pr
const pinned = content.pinned;
return pinned.filter((id) => id.trim().length > 0);
} catch (err: unknown) {
const errObj = err as { statusCode?: number; body?: { errcode?: string } };
const httpStatus = errObj.statusCode;
const errcode = errObj.body?.errcode;
if (httpStatus === 404 || errcode === "M_NOT_FOUND") {
if (isMatrixNotFoundError(err)) {
return [];
}
throw err;

View File

@@ -0,0 +1,10 @@
export function isMatrixNotFoundError(err: unknown): boolean {
const errObj = err as { statusCode?: number; body?: { errcode?: string } };
if (errObj?.statusCode === 404 || errObj?.body?.errcode === "M_NOT_FOUND") {
return true;
}
const message = (err instanceof Error ? err.message : String(err)).toLowerCase();
return (
message.includes("m_not_found") || message.includes("[404]") || message.includes("not found")
);
}

View File

@@ -105,6 +105,33 @@ describe("createMatrixRoomInfoResolver", () => {
});
});
it("treats missing room metadata as resolved-empty state", async () => {
const client = {
getRoomStateEvent: vi.fn(async (_roomId: string, eventType: string) => {
if (eventType === "m.room.name" || eventType === "m.room.canonical_alias") {
const err = new Error("M_NOT_FOUND");
Object.assign(err, {
statusCode: 404,
body: { errcode: "M_NOT_FOUND" },
});
throw err;
}
return {};
}),
} as unknown as MatrixClient & {
getRoomStateEvent: ReturnType<typeof vi.fn>;
};
const resolver = createMatrixRoomInfoResolver(client);
await expect(
resolver.getRoomInfo("!room:example.org", { includeAliases: true }),
).resolves.toEqual({
altAliases: [],
aliasesResolved: true,
nameResolved: true,
});
});
it("retries room metadata after a transient lookup failure", async () => {
const client = {
getRoomStateEvent: vi.fn(async (_roomId: string, eventType: string) => {

View File

@@ -1,3 +1,4 @@
import { isMatrixNotFoundError } from "../errors.js";
import type { MatrixClient } from "../sdk.js";
export type MatrixRoomInfo = {
@@ -43,8 +44,10 @@ export function createMatrixRoomInfoResolver(client: MatrixClient) {
if (nameState && typeof nameState.name === "string") {
name = nameState.name;
}
} catch {
// ignore
} catch (err) {
if (isMatrixNotFoundError(err)) {
nameResolved = true;
}
}
const info = { name, nameResolved };
if (nameResolved) {
@@ -73,8 +76,10 @@ export function createMatrixRoomInfoResolver(client: MatrixClient) {
if (Array.isArray(rawAliases)) {
altAliases = rawAliases.filter((entry): entry is string => typeof entry === "string");
}
} catch {
// ignore
} catch (err) {
if (isMatrixNotFoundError(err)) {
aliasesResolved = true;
}
}
const info = { canonicalAlias, altAliases, aliasesResolved };
if (aliasesResolved) {

View File

@@ -16,6 +16,7 @@ import type { SsrFPolicy } from "../runtime-api.js";
import { resolveMatrixRoomKeyBackupReadinessError } from "./backup-health.js";
import { FileBackedMatrixSyncStore } from "./client/file-sync-store.js";
import { createMatrixJsSdkClientLogger } from "./client/logging.js";
import { isMatrixNotFoundError } from "./errors.js";
import { MatrixCryptoBootstrapper } from "./sdk/crypto-bootstrap.js";
import type { MatrixCryptoBootstrapResult } from "./sdk/crypto-bootstrap.js";
import { createMatrixCryptoFacade, type MatrixCryptoFacade } from "./sdk/crypto-facade.js";
@@ -145,17 +146,6 @@ function normalizeOptionalString(value: string | null | undefined): string | nul
return normalized ? normalized : null;
}
function isMatrixNotFoundError(err: unknown): boolean {
const errObj = err as { statusCode?: number; body?: { errcode?: string } };
if (errObj?.statusCode === 404 || errObj?.body?.errcode === "M_NOT_FOUND") {
return true;
}
const message = (err instanceof Error ? err.message : String(err)).toLowerCase();
return (
message.includes("m_not_found") || message.includes("[404]") || message.includes("not found")
);
}
function isUnsupportedAuthenticatedMediaEndpointError(err: unknown): boolean {
const statusCode = (err as { statusCode?: number })?.statusCode;
if (statusCode === 404 || statusCode === 405 || statusCode === 501) {