{"record":{"id":"a29da6398e871828","repo":"HeyPuter/puter","slug":"subdomain-not-owned","errorCode":"subdomain_not_owned","errorMessage":"Subdomain not owned by user","messagePattern":"Subdomain not owned by user","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"warning","filePath":"src/backend/drivers/apps/AppDriver.js","lineNumber":1205,"sourceCode":"        }\n    }\n\n    async #ensurePuterSiteSubdomainIsOwned(indexUrl, user) {\n        if (!user) return;\n        const subdomain = this.#extractPuterHostedSubdomain(indexUrl);\n        if (!subdomain) return;\n\n        let row = await this.stores.subdomain.getBySubdomain(subdomain);\n        if (!row) {\n            // Deploys create the subdomain and immediately point the app\n            // at it, so a replica or peer-cache miss here would wrongly\n            // refuse the owner. Confirm against the primary before failing.\n            row = await this.stores.subdomain.getBySubdomain(subdomain, {\n                primary: true,\n            });\n        }\n        if (!row || row.user_id !== user.id) {\n            throw new HttpError(400, 'Subdomain not owned by user', {\n                legacyCode: 'subdomain_not_owned',\n                fields: { subdomain },\n            });\n        }\n    }\n\n    /**\n     * Origin-bootstrap detection: rows auto-created when an unknown origin\n     * first needed an app row (no human-supplied metadata). Marker is `name ===\n     * uid && title === uid` and a description starting with \"App created from\n     * origin \". Only these rows are eligible for same-owner merging — refusing\n     * to merge arbitrary same-owner apps prevents accidental data loss.\n     */\n    #isOriginBootstrapApp(app) {\n        if (!app || typeof app !== 'object') return false;\n        if (typeof app.uid !== 'string' || !app.uid) return false;\n        if (app.name !== app.uid) return false;\n        if (app.title !== app.uid) return false;","sourceCodeStart":1187,"sourceCodeEnd":1223,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/apps/AppDriver.js#L1187-L1223","documentation":"Thrown by `#ensurePuterSiteSubdomainIsOwned` when the index_url points at a puter.site subdomain whose `subdomain` row is either absent (even after a primary-store fallback check) or owned by a different user. Returns HTTP 400 with legacyCode `subdomain_not_owned`, carrying `fields.subdomain`.","triggerScenarios":"Creating/updating an app whose index_url uses a `<sub>.puter.site` host the acting user doesn't own. The store first checks replica/cache, then the primary store, to avoid false negatives during deploys.","commonSituations":"Pointing an app at another user's deployed puter.site subdomain; a stale cache miss on a freshly created subdomain during a deploy; typo in the subdomain name.","solutions":["Deploy to a subdomain you own.","Register/own the target subdomain first, then point the app at it.","If the deploy just created it, retry once — the primary fallback should resolve it; if not, verify the subdomain name."],"exampleFix":"// before\nobject.index_url = 'https://someoneelse.puter.site/';\n\n// after\nobject.index_url = `https://${mySubdomain}.puter.site/`;","handlingStrategy":"validation","validationCode":"const sub = extractPuterSubdomain(indexUrl); // e.g. 'foo' for https://foo.puter.site\nif (sub) {\n  const row = await stores.subdomain.getBySubdomain(sub);\n  if (!row || row.user_id !== user.id) throw new Error('subdomain not owned');\n}\nawait driver.create({ object: { index_url } });","typeGuard":null,"tryCatchPattern":"try {\n  await driver.create({ object: { index_url } });\n} catch (e) {\n  if (e.code === 'subdomain_not_owned') { index_url = `https://${mySub}.puter.site/`; /* retry once */ return; }\n  throw e;\n}","preventionTips":["Only point apps at puter.site subdomains you own.","Register the subdomain before referencing it in index_url.","On a fresh deploy, retry once to let the primary store catch up."],"tags":["apps","index-url","subdomain","validation","driver"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}