{"record":{"id":"7eb927730e7adaba","repo":"hcengineering/platform","slug":"missing-config-for-attributes-missingenv-join-7eb927","errorCode":null,"errorMessage":"Missing config for attributes: ${missingEnv.join(', ')}","messagePattern":"Missing config for attributes: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"services/billing/pod-billing/src/config.ts","lineNumber":40,"sourceCode":"  UsageUpdateInterval: number // seconds\n}\n\nconst parseNumber = (str: string | undefined): number | undefined => (str !== undefined ? Number(str) : undefined)\n\nconst config: Config = (() => {\n  const params: Partial<Config> = {\n    Port: parseNumber(process.env.PORT) ?? 4040,\n    Secret: process.env.SECRET,\n    AccountsUrl: process.env.ACCOUNTS_URL,\n    DbUrl: process.env.DB_URL,\n    StorageConfig: process.env.STORAGE_CONFIG,\n    UsageUpdateInterval: parseNumber(process.env.USAGE_UPDATE_INTERVAL) ?? 60 * 60\n  }\n\n  const missingEnv = (Object.keys(params) as Array<keyof Config>).filter((key) => params[key] === undefined)\n\n  if (missingEnv.length > 0) {\n    throw Error(`Missing config for attributes: ${missingEnv.join(', ')}`)\n  }\n\n  return params as Config\n})()\n\nexport default config\n","sourceCodeStart":22,"sourceCodeEnd":47,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/billing/pod-billing/src/config.ts#L22-L47","documentation":"The pod-billing config IIFE validates that no Config field is undefined, throwing 'Missing config for attributes' on import. With Port (4040) and UsageUpdateInterval (3600s) defaulted, the error can only be raised by SECRET, ACCOUNTS_URL, DB_URL, or STORAGE_CONFIG being unset.","triggerScenarios":"Importing services/billing/pod-billing/src/config.ts without one of: SECRET, ACCOUNTS_URL, DB_URL, STORAGE_CONFIG. The message lists the Config keys, e.g. 'Secret, DbUrl'.","commonSituations":"Deploying the billing service without its database URL or storage config; secret not mounted; forgetting ACCOUNTS_URL after moving accounts to a dedicated host; local run without .env.","solutions":["Set the env vars named in the message: SECRET, ACCOUNTS_URL, DB_URL, STORAGE_CONFIG.","Verify DB_URL points at the billing database and SECRET matches the accounts service value.","Check ConfigMap/Secret mounts and exact env key names in the deployment.","Add a local .env with the four vars before starting the pod."],"exampleFix":"// before\nDB_URL=postgres://...  # STORAGE_CONFIG missing\n// after\nSECRET=...\nACCOUNTS_URL=https://accounts.example.com\nDB_URL=postgres://billing:5432/billing\nSTORAGE_CONFIG={\"name\":\"minio\",\"kind\":\"s3\",...}","handlingStrategy":"validation","validationCode":"const missing = ['SECRET','ACCOUNTS_URL','DB_URL','STORAGE_CONFIG'].filter((k) => process.env[k] === undefined)\nif (missing.length > 0) throw new Error(`Missing config for attributes: ${missing.join(', ')}`)","typeGuard":"function hasEnv(key: string): boolean {\n  return process.env[key] !== undefined\n}","tryCatchPattern":"try {\n  const { default: config } = await import('./config.js')\n} catch (err) {\n  if ((err as Error).message.startsWith('Missing config for attributes:')) {\n    console.error('billing config error:', err.message)\n    process.exit(1)\n  }\n  throw err\n}","preventionTips":["List SECRET, ACCOUNTS_URL, DB_URL, STORAGE_CONFIG in .env.example for pod-billing.","Verify DB connectivity and storage config validity before rollout.","Share SECRET via the platform k8s Secret across services.","Add a config preflight check to the deploy pipeline."],"tags":["configuration","environment-variables","billing"],"backgroundTag":"missing-env-var","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}