@@ -411,7 +411,13 @@ describe("official external plugin catalog", () => {
411411 } ) ;
412412 const seeded = await loadHostedOfficialExternalPluginCatalogEntries ( {
413413 snapshotStore : createInMemoryHostedOfficialExternalPluginCatalogSnapshotStore ( ) ,
414- fetchImpl : vi . fn ( async ( ) => new Response ( body , { status : 200 } ) ) ,
414+ fetchImpl : vi . fn (
415+ async ( ) =>
416+ new Response ( body , {
417+ status : 200 ,
418+ headers : { etag : '"snapshot-v1"' } ,
419+ } ) ,
420+ ) ,
415421 } ) ;
416422 if ( seeded . source !== "hosted" ) {
417423 throw new Error ( "expected seeded hosted feed" ) ;
@@ -426,7 +432,14 @@ describe("official external plugin catalog", () => {
426432
427433 const result = await loadHostedOfficialExternalPluginCatalogEntries ( {
428434 snapshotStore,
429- fetchImpl : vi . fn ( async ( ) => new Response ( null , { status : 304 } ) ) ,
435+ ifNoneMatch : '"snapshot-v1"' ,
436+ fetchImpl : vi . fn (
437+ async ( ) =>
438+ new Response ( null , {
439+ status : 304 ,
440+ headers : { etag : '"snapshot-v1"' } ,
441+ } ) ,
442+ ) ,
430443 } ) ;
431444
432445 expect ( result . source ) . toBe ( "hosted-snapshot" ) ;
@@ -438,6 +451,60 @@ describe("official external plugin catalog", () => {
438451 }
439452 } ) ;
440453
454+ it ( "does not use a stale snapshot when HTTP 304 validators do not match" , async ( ) => {
455+ const body = JSON . stringify ( {
456+ schemaVersion : 1 ,
457+ id : "openclaw-official-external-plugins" ,
458+ generatedAt : "2026-06-22T00:00:00.000Z" ,
459+ sequence : 4 ,
460+ entries : [
461+ {
462+ name : "@openclaw/stale-snapshot-proof" ,
463+ kind : "plugin" ,
464+ openclaw : { plugin : { id : "stale-snapshot-proof" } } ,
465+ } ,
466+ ] ,
467+ } ) ;
468+ const seeded = await loadHostedOfficialExternalPluginCatalogEntries ( {
469+ snapshotStore : createInMemoryHostedOfficialExternalPluginCatalogSnapshotStore ( ) ,
470+ fetchImpl : vi . fn (
471+ async ( ) =>
472+ new Response ( body , {
473+ status : 200 ,
474+ headers : { etag : '"snapshot-v1"' } ,
475+ } ) ,
476+ ) ,
477+ } ) ;
478+ if ( seeded . source !== "hosted" ) {
479+ throw new Error ( "expected seeded hosted feed" ) ;
480+ }
481+ const snapshotStore = createInMemoryHostedOfficialExternalPluginCatalogSnapshotStore ( [
482+ {
483+ body,
484+ metadata : seeded . metadata ,
485+ savedAt : "2026-06-22T01:02:03.000Z" ,
486+ } ,
487+ ] ) ;
488+
489+ const result = await loadHostedOfficialExternalPluginCatalogEntries ( {
490+ snapshotStore,
491+ ifNoneMatch : '"snapshot-v2"' ,
492+ fetchImpl : vi . fn (
493+ async ( ) =>
494+ new Response ( null , {
495+ status : 304 ,
496+ headers : { etag : '"snapshot-v2"' } ,
497+ } ) ,
498+ ) ,
499+ } ) ;
500+
501+ expect ( result . source ) . toBe ( "bundled-fallback" ) ;
502+ if ( result . source === "bundled-fallback" ) {
503+ expect ( result . error ) . toContain ( "snapshot fallback failed" ) ;
504+ expect ( result . error ) . toContain ( "ETag" ) ;
505+ }
506+ } ) ;
507+
441508 it ( "uses a valid snapshot before bundled fallback when hosted validation fails" , async ( ) => {
442509 const body = JSON . stringify ( {
443510 schemaVersion : 1 ,
@@ -477,6 +544,46 @@ describe("official external plugin catalog", () => {
477544 }
478545 } ) ;
479546
547+ it ( "does not use a stale snapshot when hosted validation fails with unmatched validators" , async ( ) => {
548+ const body = JSON . stringify ( {
549+ schemaVersion : 1 ,
550+ id : "openclaw-official-external-plugins" ,
551+ generatedAt : "2026-06-22T00:00:00.000Z" ,
552+ sequence : 5 ,
553+ entries : [
554+ {
555+ name : "@openclaw/stale-validation-snapshot-proof" ,
556+ kind : "plugin" ,
557+ openclaw : { plugin : { id : "stale-validation-snapshot-proof" } } ,
558+ } ,
559+ ] ,
560+ } ) ;
561+ const seeded = await loadHostedOfficialExternalPluginCatalogEntries ( {
562+ snapshotStore : createInMemoryHostedOfficialExternalPluginCatalogSnapshotStore ( ) ,
563+ fetchImpl : vi . fn (
564+ async ( ) => new Response ( body , { status : 200 , headers : { etag : '"snapshot-v1"' } } ) ,
565+ ) ,
566+ } ) ;
567+ if ( seeded . source !== "hosted" ) {
568+ throw new Error ( "expected seeded hosted feed" ) ;
569+ }
570+ const snapshotStore = createInMemoryHostedOfficialExternalPluginCatalogSnapshotStore ( [
571+ { body, metadata : seeded . metadata , savedAt : "2026-06-22T01:02:03.000Z" } ,
572+ ] ) ;
573+
574+ const result = await loadHostedOfficialExternalPluginCatalogEntries ( {
575+ snapshotStore,
576+ ifNoneMatch : '"snapshot-v2"' ,
577+ fetchImpl : vi . fn ( async ( ) => new Response ( "{ nope" , { status : 200 } ) ) ,
578+ } ) ;
579+
580+ expect ( result . source ) . toBe ( "bundled-fallback" ) ;
581+ if ( result . source === "bundled-fallback" ) {
582+ expect ( result . error ) . toContain ( "snapshot fallback failed" ) ;
583+ expect ( result . error ) . toContain ( "ETag" ) ;
584+ }
585+ } ) ;
586+
480587 it ( "falls back to bundled entries when the snapshot is invalid" , async ( ) => {
481588 const snapshotStore = createInMemoryHostedOfficialExternalPluginCatalogSnapshotStore ( [
482589 {
0 commit comments