{"record":{"id":"315937fd513fe4a7","repo":"hcengineering/platform","slug":"missing-config-for-attributes-missingenv-join-315937","errorCode":null,"errorMessage":"Missing config for attributes: ${missingEnv.join(', ')}","messagePattern":"Missing config for attributes: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"services/analytics-collector/pod-analytics-collector/src/config.ts","lineNumber":48,"sourceCode":"const 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) ?? 4007,\n    Secret: process.env.SECRET,\n    ServiceID: process.env.SERVICE_ID ?? 'analytics-collector-service',\n    AccountsUrl: process.env.ACCOUNTS_URL,\n    PostHogHost: process.env.POSTHOG_HOST,\n    PostHogAPI: process.env.POSTHOG_API_KEY,\n    MaxPayloadSize: process.env.MAX_PAYLOAD_SIZE ?? '10mb'\n  }\n\n  const requiredParams = ['Secret', 'AccountsUrl'] as Array<keyof Config>\n\n  const missingEnv = requiredParams.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":30,"sourceCodeEnd":55,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/analytics-collector/pod-analytics-collector/src/config.ts#L30-L55","documentation":"The analytics-collector config IIFE requires the 'Secret' and 'AccountsUrl' params (checked against requiredParams) and throws 'Missing config for attributes' when either is undefined. This ensures the collector can authenticate incoming events and talk to the accounts service.","triggerScenarios":"Importing services/analytics-collector/pod-analytics-collector/src/config.ts without SECRET or ACCOUNTS_URL env vars set. The message names the attribute(s): 'Secret' and/or 'AccountsUrl'.","commonSituations":"Deploying analytics-collector without the shared SECRET; forgetting ACCOUNTS_URL when accounts runs at a custom endpoint; secret not mounted or renamed in the deployment manifest.","solutions":["Set SECRET and ACCOUNTS_URL in the collector's environment.","Verify the Secret object holding SECRET is mounted and key names match exactly.","Add ACCOUNTS_URL to the service ConfigMap/env block.","Use the same SECRET value as the accounts service, otherwise authenticated requests will later fail."],"exampleFix":"// before\nenv: { PORT: \"4007\" }  # SECRET/ACCOUNTS_URL absent\n// after\nenv:\n  - name: SECRET\n    valueFrom: { secretKeyRef: { name: huly-secret, key: SECRET } }\n  - name: ACCOUNTS_URL\n    value: https://accounts.example.com","handlingStrategy":"validation","validationCode":"const missing = ['SECRET', 'ACCOUNTS_URL'].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('analytics-collector config error:', err.message)\n    process.exit(1)\n  }\n  throw err\n}","preventionTips":["Include SECRET and ACCOUNTS_URL in the collector's deployment env by default.","Share the SECRET via the same k8s Secret used by the accounts service.","Add ACCOUNTS_URL to the ConfigMap whenever the accounts endpoint changes.","Run a CI smoke test that imports the config module."],"tags":["configuration","environment-variables","analytics"],"backgroundTag":"missing-env-var","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}