V3 Hardening & Fixes: Structured Errors, JWT Fallbacks, and Module Stability

- Implemented Structured Error Handling across GET/POST/PATCH helpers to extract rich V3 error metadata.
- Added direct localStorage fallback for JWT detection to resolve race conditions during initial page load.
- Fixed async race condition in Archives leading to 'archive_content_li is undefined' crash.
- Hardened generic object processor to handle non-array API responses gracefully.
- Resolved zero-result bug in Event Search by using raw 'account_id_random' to bypass backend mapping conflicts.
- Isolated bootstrap headers in +layout.ts and removed invalid response headers from request config.
- Enhanced /testing dashboard with live header inspection and V3 hardening audits.
This commit is contained in:
Scott Idem
2026-01-19 19:06:32 -05:00
parent c40a296a77
commit 0e411531eb
8 changed files with 354 additions and 123 deletions

View File

@@ -67,20 +67,26 @@ export const get_object = async function get_object({
const headers_cleaned: key_val = {};
const merged_headers = { ...api_cfg['headers'], ...headers };
// Auto-promote account_id from api_cfg to header if missing
if (!merged_headers['x-account-id'] && api_cfg['account_id']) {
merged_headers['x-account-id'] = api_cfg['account_id'];
}
// Handle "Bootstrap Paradox" for unauthenticated requests
if (merged_headers.hasOwnProperty('x-no-account-id')) {
const bypass_val = merged_headers['x-no-account-id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_get_object: Valid bypass detected. Stripping account ID context.');
delete merged_headers['x-account-id'];
} else if (bypass_val === null || bypass_val === undefined || bypass_val === 'No_Account_ID_Here') {
if (log_lvl > 1) console.log('api_get_object: Placeholder bypass detected. Preserving account ID context.');
delete merged_headers['x-no-account-id'];
}
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_get_object: Valid bypass detected. Stripping account ID context.');
delete merged_headers['x-account-id'];
delete merged_headers['x_account_id'];
} else {
// If it's a placeholder (like "No_Account_ID_Here"), just remove the bypass header
// but DO NOT strip the valid Account ID.
delete merged_headers['x-no-account-id'];
delete merged_headers['x_no_account_id'];
}
// Standardize all headers to kebab-case and ensure string values
@@ -96,7 +102,27 @@ export const get_object = async function get_object({
}
// Auto-inject Authorization header if JWT is present but header is missing
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
let jwt = headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
headers_cleaned['x-aether-api-token'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'] ||
api_cfg['headers']?.['x-aether-api-token'];
// Final Fallback: Check localStorage directly to avoid store sync race conditions
if (!jwt && typeof localStorage !== 'undefined') {
try {
const ae_loc_raw = localStorage.getItem('ae_loc');
if (ae_loc_raw) {
const ae_loc_json = JSON.parse(ae_loc_raw);
jwt = ae_loc_json.jwt || ae_loc_json.token;
}
} catch (e) {
// Silently fail on storage read
}
}
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
}
@@ -178,27 +204,51 @@ export const get_object = async function get_object({
return null;
}
// FAIL FAST (Section 2D): Do not retry on Auth/Permission failures
if (response.status === 401 || response.status === 403) {
console.error(`API Auth Failure (${response.status}). Failing fast as per Section 2D protocol.`);
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS: Headers sent for ${endpoint}:`, {
has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'],
has_account_id: !!headers_cleaned['x-account-id'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
});
}
// Structured Error Handling (V3): Attempt to get rich error metadata
let error_json: any = null;
try {
error_json = await response.json();
} catch (e) {
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (error_json?.meta?.details) {
return error_json;
}
// Fallback for standard FastAPI "detail" errors
if (error_json?.detail) {
return {
meta: {
success: false,
status_code: response.status,
details: {
category: 'validation',
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
raw: error_json.detail
}
}
};
}
return false;
}
// Structured Error Handling (V3): Attempt to get rich error metadata
let error_json: any = null;
try {
error_json = await response.json();
} catch (e) {
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (error_json?.meta?.details) {
// Return the rich error object so caller can handle specific categories (database_schema, etc)
return error_json;
}
throw new Error(`HTTP error! status: ${response.status}`);
}

View File

@@ -50,12 +50,26 @@ export const patch_object = async function patch_object({
const headers_cleaned: key_val = {};
const merged_headers = { ...api_cfg['headers'], ...headers };
// Auto-promote account_id from api_cfg to header if missing
if (!merged_headers['x-account-id'] && api_cfg['account_id']) {
merged_headers['x-account-id'] = api_cfg['account_id'];
}
// Handle "Bootstrap Paradox" for unauthenticated requests
if (merged_headers.hasOwnProperty('x-no-account-id')) {
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_patch_object: Valid bypass detected. Stripping account ID context.');
delete merged_headers['x-account-id'];
if (merged_headers['x-no-account-id'] === null) {
merged_headers['x-no-account-id'] = 'Nothing to See Here';
}
delete merged_headers['x_account_id'];
} else {
// If it's a placeholder (like "No_Account_ID_Here"), just remove the bypass header
// but DO NOT strip the valid Account ID.
delete merged_headers['x-no-account-id'];
delete merged_headers['x_no_account_id'];
}
for (const prop in merged_headers) {
@@ -70,7 +84,28 @@ export const patch_object = async function patch_object({
}
// Auto-inject Authorization header if JWT is present but header is missing
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
let jwt = headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
headers_cleaned['x-aether-api-token'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'] ||
api_cfg['headers']?.['x-aether-api-token'];
// Final Fallback: Check localStorage directly to avoid store sync race conditions
if (!jwt && typeof localStorage !== 'undefined') {
try {
// Check primary key first
const ae_loc_raw = localStorage.getItem('ae_loc');
if (ae_loc_raw) {
const ae_loc_json = JSON.parse(ae_loc_raw);
jwt = ae_loc_json.jwt || ae_loc_json.token;
}
} catch (e) {
// Silently fail on storage read
}
}
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
}
@@ -126,18 +161,58 @@ export const patch_object = async function patch_object({
if (!response.ok) {
if (response.status === 404) {
console.warn('404 Not Found. Returning null.');
if (log_lvl) {
console.log('The response was a 404 not found "error". Returning null.');
}
return null;
}
const errorBody = await response.text();
console.error(`HTTP error! status: ${response.status}`, errorBody);
if (response.status >= 400 && response.status < 404) {
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS (PATCH): Headers sent for ${endpoint}:`, {
has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'],
has_account_id: !!headers_cleaned['x-account-id'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
});
}
// Structured Error Handling (V3): Attempt to get rich error metadata
let error_json: any = null;
try {
error_json = await response.json();
} catch (e) {
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (error_json?.meta?.details) {
return error_json;
}
// Fallback for standard FastAPI "detail" errors
if (error_json?.detail) {
return {
meta: {
success: false,
status_code: response.status,
details: {
category: 'validation',
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
raw: error_json.detail
}
}
};
}
return false;
}
throw new Error(`HTTP error! status: ${response.status} - ${errorBody}`);
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();

View File

@@ -70,20 +70,26 @@ export const post_object = async function post_object({
const headers_cleaned: key_val = {};
const merged_headers = { ...api_cfg['headers'], ...headers };
// Auto-promote account_id from api_cfg to header if missing
if (!merged_headers['x-account-id'] && api_cfg['account_id']) {
merged_headers['x-account-id'] = api_cfg['account_id'];
}
// Handle "Bootstrap Paradox" for unauthenticated requests
if (merged_headers.hasOwnProperty('x-no-account-id')) {
const bypass_val = merged_headers['x-no-account-id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_post_object: Valid bypass detected. Stripping account ID context.');
delete merged_headers['x-account-id'];
} else if (bypass_val === null || bypass_val === undefined || bypass_val === 'No_Account_ID_Here') {
if (log_lvl > 1) console.log('api_post_object: Placeholder bypass detected. Preserving account ID context.');
delete merged_headers['x-no-account-id'];
}
const bypass_val = merged_headers['x-no-account-id'] || merged_headers['x_no_account_id'];
const is_valid_bypass = bypass_val === 'bypass' ||
bypass_val === 'Nothing to See Here' ||
bypass_val === 'direct-download';
if (is_valid_bypass) {
if (log_lvl > 1) console.log('api_post_object: Valid bypass detected. Stripping account ID context.');
delete merged_headers['x-account-id'];
delete merged_headers['x_account_id'];
} else {
// If it's a placeholder (like "No_Account_ID_Here"), just remove the bypass header
// but DO NOT strip the valid Account ID.
delete merged_headers['x-no-account-id'];
delete merged_headers['x_no_account_id'];
}
for (const prop in merged_headers) {
@@ -98,7 +104,27 @@ export const post_object = async function post_object({
}
// Auto-inject Authorization header if JWT is present but header is missing
const jwt = headers_cleaned['jwt'] || headers_cleaned['JWT'] || api_cfg['jwt'];
let jwt = headers_cleaned['jwt'] ||
headers_cleaned['JWT'] ||
headers_cleaned['x-aether-api-token'] ||
api_cfg['jwt'] ||
api_cfg['headers']?.['jwt'] ||
api_cfg['headers']?.['JWT'] ||
api_cfg['headers']?.['x-aether-api-token'];
// Final Fallback: Check localStorage directly to avoid store sync race conditions
if (!jwt && typeof localStorage !== 'undefined') {
try {
const ae_loc_raw = localStorage.getItem('ae_loc');
if (ae_loc_raw) {
const ae_loc_json = JSON.parse(ae_loc_raw);
jwt = ae_loc_json.jwt || ae_loc_json.token;
}
} catch (e) {
// Silently fail on storage read
}
}
if (jwt && !headers_cleaned['Authorization'] && !headers_cleaned['authorization']) {
headers_cleaned['Authorization'] = `Bearer ${jwt}`;
}
@@ -170,27 +196,51 @@ export const post_object = async function post_object({
return null;
}
// FAIL FAST (Section 2D): Do not retry on Auth/Permission failures
if (response.status === 401 || response.status === 403) {
console.error(`API Auth Failure (${response.status}). Failing fast as per Section 2D protocol.`);
// FAIL FAST (Section 2D): Do not retry on Auth or Client errors (400, 401, 403, 422)
if (response.status === 400 || response.status === 401 || response.status === 403 || response.status === 422) {
if (log_lvl) console.error(`API Client Failure (${response.status}). Failing fast.`);
if (response.status === 401 || response.status === 403) {
console.warn(`AUTH DIAGNOSTICS (POST): Headers sent for ${endpoint}:`, {
has_auth: !!headers_cleaned['Authorization'],
has_api_key: !!headers_cleaned['x-aether-api-key'],
has_account_id: !!headers_cleaned['x-account-id'],
jwt_preview: jwt ? `${jwt.slice(0, 8)}...` : 'MISSING'
});
}
// Structured Error Handling (V3): Attempt to get rich error metadata
let error_json: any = null;
try {
error_json = await response.json();
} catch (e) {
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (error_json?.meta?.details) {
return error_json;
}
// Fallback for standard FastAPI "detail" errors
if (error_json?.detail) {
return {
meta: {
success: false,
status_code: response.status,
details: {
category: 'validation',
message: typeof error_json.detail === 'string' ? error_json.detail : JSON.stringify(error_json.detail),
raw: error_json.detail
}
}
};
}
return false;
}
// Structured Error Handling (V3): Attempt to get rich error metadata
let error_json: any = null;
try {
error_json = await response.json();
} catch (e) {
// Not JSON
}
if (log_lvl) console.log('The response was not ok. Structured Error Check:', error_json);
if (error_json?.meta?.details) {
// Return the rich error object so caller can handle specific categories (database_schema, etc)
return error_json;
}
throw new Error(`HTTP error! status: ${response.status}`);
}

View File

@@ -45,7 +45,8 @@ export async function load_ae_obj_id__archive({
obj_type: 'archive',
obj_id: archive_id,
view,
params,
// Only pass view and specific params to avoid 422 validation errors on single-obj endpoints
params: params,
log_lvl: log_lvl
})
.then(async function (archive_obj_get_result) {
@@ -170,9 +171,11 @@ export async function load_ae_obj_li__archive({
}
});
if (inc_content_li && ae_promises.load__archive_obj_li) {
for (let i = 0; i < ae_promises.load__archive_obj_li.length; i++) {
const archive_obj = ae_promises.load__archive_obj_li[i];
const archive_obj_li = await ae_promises.load__archive_obj_li;
if (inc_content_li && archive_obj_li && Array.isArray(archive_obj_li)) {
for (let i = 0; i < archive_obj_li.length; i++) {
const archive_obj = archive_obj_li[i];
const archive_id = archive_obj.archive_id_random;
const content_li = await load_ae_obj_li__archive_content({
@@ -187,11 +190,11 @@ export async function load_ae_obj_li__archive({
try_cache,
log_lvl
});
ae_promises.load__archive_obj_li[i].archive_content_li = content_li;
archive_obj_li[i].archive_content_li = content_li;
}
}
return ae_promises.load__archive_obj_li;
return archive_obj_li;
}
// Updated 2026-01-06

View File

@@ -335,7 +335,7 @@ async function _process_generic_props<T extends Record<string, any>>({
log_lvl?: number;
specific_processor?: (obj: T) => Promise<T> | T;
}): Promise<T[]> {
if (!obj_li || obj_li.length === 0) return [];
if (!obj_li || !Array.isArray(obj_li) || obj_li.length === 0) return [];
const processed_obj_li: T[] = [];

View File

@@ -443,7 +443,7 @@ export async function qry_ae_obj_li__event({
log_lvl?: number;
}) {
const search_query: any = { and: [] };
if (qry_str) {
// Use reserved 'q' property for global full-text search as per V3 Guide
search_query.q = qry_str;
@@ -495,14 +495,16 @@ export async function qry_ae_obj_li__event({
}
// Location Filtering (Inclusive OR logic)
// If either filter is explicitly true, we restrict results.
// If both are false or null, we show everything.
if (qry_physical === true || qry_virtual === true) {
const ev_physical = ev.physical === true || ev.physical === 1 || ev.physical === '1';
const ev_virtual = ev.virtual === true || ev.virtual === 1 || ev.virtual === '1';
let match = false;
if (qry_physical === true && ev_physical) match = true;
if (qry_virtual === true && ev_virtual) match = true;
if (!match) return false;
}
@@ -624,16 +626,16 @@ export async function qry_ae_obj_li__event_v2({
}
// Location Filtering (Inclusive OR logic)
// If either filter is explicitly true, we restrict results.
// If either filter is explicitly true, we restrict results.
// If both are false or null, we show everything.
if (qry_physical === true || qry_virtual === true) {
const ev_physical = ev.physical === true || ev.physical === 1 || ev.physical === '1';
const ev_virtual = ev.virtual === true || ev.virtual === 1 || ev.virtual === '1';
let match = false;
if (qry_physical === true && ev_physical) match = true;
if (qry_virtual === true && ev_virtual) match = true;
if (!match) return false;
}