Skip to content
This repository was archived by the owner on Aug 25, 2026. It is now read-only.

Commit 9bbc7e5

Browse files
TML-3103: derive error docsUrl from docsUrlFor per-code anchors
The CliStructuredError factories in @prisma-next/errors and the CLI init errors hardcoded prisma-next.dev command-page URLs predating docsUrlFor. All 23 docsUrl fields now derive from docsUrlFor(code), so every code links to its own anchor on the canonical error-reference page and the at-RC DOCS_ERRORS_VERSION flip covers the whole product in one edit. The two telemetry help-text prose mentions stay until a docs-site telemetry page exists. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
1 parent e0e739c commit 9bbc7e5

7 files changed

Lines changed: 47 additions & 26 deletions

File tree

packages/1-framework/1-core/errors/src/control.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { ifDefined } from '@prisma-next/utils/defined';
22
import type { StructuredError } from '@prisma-next/utils/structured-error';
3+
import { docsUrlFor } from '@prisma-next/utils/structured-error';
34

45
/**
56
* CLI error envelope for output formatting.
@@ -125,7 +126,7 @@ export function errorConfigFileNotFound(
125126
return new CliStructuredError('CONFIG.FILE_NOT_FOUND', 'Config file not found', {
126127
...(options?.why ? { why: options.why } : { why: 'Config file not found' }),
127128
fix: "Run 'prisma-next init' to create a config file",
128-
docsUrl: 'https://prisma-next.dev/docs/cli/config',
129+
docsUrl: docsUrlFor('CONFIG.FILE_NOT_FOUND'),
129130
...(configPath ? { where: { path: configPath } } : {}),
130131
});
131132
}
@@ -139,7 +140,7 @@ export function errorContractConfigMissing(options?: {
139140
return new CliStructuredError('CONFIG.CONTRACT_MISSING', 'Contract configuration missing', {
140141
why: options?.why ?? 'The contract configuration is required for emit',
141142
fix: 'Add contract configuration to your prisma-next.config.ts',
142-
docsUrl: 'https://prisma-next.dev/docs/cli/contract-emit',
143+
docsUrl: docsUrlFor('CONFIG.CONTRACT_MISSING'),
143144
});
144145
}
145146

@@ -155,7 +156,7 @@ export function errorContractValidationFailed(
155156
return new CliStructuredError('CONTRACT.VALIDATION_FAILED', 'Contract validation failed', {
156157
why: reason,
157158
fix: 'Re-run `prisma-next contract emit`, or fix the contract file and try again',
158-
docsUrl: 'https://prisma-next.dev/docs/contracts',
159+
docsUrl: docsUrlFor('CONTRACT.VALIDATION_FAILED'),
159160
...(options?.where ? { where: options.where } : {}),
160161
});
161162
}
@@ -218,7 +219,7 @@ export function errorQueryRunnerFactoryRequired(options?: {
218219
{
219220
why: options?.why ?? 'Config.db.queryRunnerFactory is required for db verify',
220221
fix: 'Add db.queryRunnerFactory to prisma-next.config.ts',
221-
docsUrl: 'https://prisma-next.dev/docs/cli/db-verify',
222+
docsUrl: docsUrlFor('CONFIG.QUERY_RUNNER_FACTORY_REQUIRED'),
222223
},
223224
);
224225
}
@@ -235,7 +236,7 @@ export function errorFamilyReadMarkerSqlRequired(options?: {
235236
{
236237
why: options?.why ?? 'Family verify.readMarker is required for db verify',
237238
fix: 'Ensure family.verify.readMarker() is exported by your family package',
238-
docsUrl: 'https://prisma-next.dev/docs/cli/db-verify',
239+
docsUrl: docsUrlFor('CONFIG.FAMILY_READ_MARKER_REQUIRED'),
239240
},
240241
);
241242
}
@@ -269,7 +270,7 @@ export function errorDriverRequired(options?: { readonly why?: string }): CliStr
269270
{
270271
why: options?.why ?? 'Config.driver is required for DB-connected commands',
271272
fix: 'Add a control-plane driver to prisma-next.config.ts (e.g. import a driver descriptor and set `driver: postgresDriver`)',
272-
docsUrl: 'https://prisma-next.dev/docs/cli/config',
273+
docsUrl: docsUrlFor('CONFIG.DRIVER_REQUIRED'),
273274
},
274275
);
275276
}
@@ -291,7 +292,7 @@ export function errorContractMissingExtensions(options: {
291292
? `Contract requires extension pack '${missing[0]}', but CLI config does not provide a matching descriptor.`
292293
: `Contract requires extension packs ${missing.map((p) => `'${p}'`).join(', ')}, but CLI config does not provide matching descriptors.`,
293294
fix: 'Add the missing extension descriptors to `extensions` in prisma-next.config.ts',
294-
docsUrl: 'https://prisma-next.dev/docs/cli/config',
295+
docsUrl: docsUrlFor('CONFIG.MISSING_EXTENSION_PACKS'),
295296
meta: {
296297
missingExtensions: missing,
297298
providedComponentIds: [...options.providedComponentIds].sort(),
@@ -322,7 +323,7 @@ export function errorMigrationPlanningFailed(options: {
322323
why: computedWhy,
323324
fix: computedFix,
324325
meta: { conflicts: options.conflicts },
325-
docsUrl: 'https://prisma-next.dev/docs/cli/db-init',
326+
docsUrl: docsUrlFor('MIGRATION.PLANNING_FAILED'),
326327
});
327328
}
328329

@@ -338,7 +339,7 @@ export function errorTargetMigrationNotSupported(options?: {
338339
{
339340
why: options?.why ?? 'The configured target does not provide migration planner/runner',
340341
fix: 'Select a target that provides migrations (it must export `target.migrations` for db init)',
341-
docsUrl: 'https://prisma-next.dev/docs/cli/db-init',
342+
docsUrl: docsUrlFor('MIGRATION.TARGET_UNSUPPORTED'),
342343
},
343344
);
344345
}
@@ -425,7 +426,7 @@ export function errorConfigValidation(
425426
return new CliStructuredError('CONFIG.VALIDATION_FAILED', 'Config validation error', {
426427
why: options?.why ?? `Config must have a "${field}" field`,
427428
fix: 'Check your prisma-next.config.ts and ensure all required fields are provided',
428-
docsUrl: 'https://prisma-next.dev/docs/cli/config',
429+
docsUrl: docsUrlFor('CONFIG.VALIDATION_FAILED'),
429430
});
430431
}
431432

packages/1-framework/1-core/errors/test/control.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { docsUrlFor } from '@prisma-next/utils/structured-error';
12
import { describe, expect, it } from 'vitest';
23
import {
34
CliStructuredError,
@@ -144,6 +145,11 @@ describe('Config Errors', () => {
144145
expect(error.where?.path).toBe('/path/to/config.ts');
145146
});
146147

148+
it('errorConfigFileNotFound links the canonical error-reference anchor for its code', () => {
149+
const error = errorConfigFileNotFound();
150+
expect(error.docsUrl).toBe(docsUrlFor('CONFIG.FILE_NOT_FOUND'));
151+
});
152+
147153
it('errorConfigFileNotFound with custom why', () => {
148154
const error = errorConfigFileNotFound('/path/to/config.ts', { why: 'Custom reason' });
149155
expect(error.why).toBe('Custom reason');

packages/1-framework/3-tooling/cli/src/commands/init/errors.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { docsUrlFor } from '@prisma-next/utils/structured-error';
12
import { CliStructuredError } from '../../utils/cli-errors';
23

34
/**
@@ -9,7 +10,7 @@ export function errorInitReinitNeedsForce(): CliStructuredError {
910
return new CliStructuredError('CLI.INIT_REINIT_NEEDS_FORCE', 'Project is already initialized', {
1011
why: 'A `prisma-next.config.ts` already exists in this directory. Re-running `init` would overwrite the scaffolded files; in non-interactive mode `init` will not do that without `--force`.',
1112
fix: 'Pass `--force` to overwrite the existing scaffold, or run `init` interactively to confirm.',
12-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
13+
docsUrl: docsUrlFor('CLI.INIT_REINIT_NEEDS_FORCE'),
1314
});
1415
}
1516

@@ -44,7 +45,7 @@ export function errorInitMissingFlags(options: {
4445
return new CliStructuredError('CLI.INIT_MISSING_FLAGS', 'Missing required flags', {
4546
why: `${options.why} Missing required flag(s): ${flagList}.`,
4647
fix: `Re-run with the missing flag(s) supplied, e.g. \`prisma-next init --yes ${fixList}\`. Use \`prisma-next init --help\` to see every flag.`,
47-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
48+
docsUrl: docsUrlFor('CLI.INIT_MISSING_FLAGS'),
4849
meta: { missingFlags: options.missing },
4950
});
5051
}
@@ -64,7 +65,7 @@ export function errorInitInvalidFlagValue(options: {
6465
{
6566
why: `\`--${options.flag} ${options.value}\` is not one of: ${options.allowed.join(', ')}.`,
6667
fix: `Use one of: ${options.allowed.map((v) => `--${options.flag} ${v}`).join(', ')}.`,
67-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
68+
docsUrl: docsUrlFor('CLI.INIT_INVALID_FLAG_VALUE'),
6869
meta: { flag: options.flag, value: options.value, allowed: options.allowed },
6970
},
7071
);
@@ -93,7 +94,7 @@ export function errorInitAuthoringSchemaPathMismatch(options: {
9394
`Use a matching pair, for example \`--authoring ${expectedAuthoring} --schema-path <path>${options.expectedExtension}\`, ` +
9495
'or change `--authoring` to match the path you supplied. ' +
9596
'You can also omit `--schema-path` to use the default for the chosen authoring.',
96-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
97+
docsUrl: docsUrlFor('CLI.INIT_AUTHORING_SCHEMA_PATH_MISMATCH'),
9798
meta: {
9899
authoring: options.authoring,
99100
schemaPath: options.schemaPath,
@@ -134,7 +135,7 @@ export function errorInitStrictProbeWithoutProbe(): CliStructuredError {
134135
{
135136
why: '`--strict-probe` only changes how a *failed* probe is reported; without `--probe-db` no probe is attempted in the first place. (`init` is offline-by-default — it never opens a connection to your database without explicit consent.)',
136137
fix: 'Add `--probe-db` to opt in to the probe, or drop `--strict-probe` if you do not need the version check.',
137-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
138+
docsUrl: docsUrlFor('CLI.INIT_STRICT_PROBE_WITHOUT_PROBE'),
138139
},
139140
);
140141
}
@@ -161,7 +162,7 @@ export function errorInitInstallFailed(options: {
161162
return new CliStructuredError('CLI.INIT_INSTALL_FAILED', 'Failed to install dependencies', {
162163
why,
163164
fix: `Install manually:\n ${options.addCommand}\n ${options.addDevCommand}\nThen run \`${options.emitCommand}\` to emit the contract.`,
164-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
165+
docsUrl: docsUrlFor('CLI.INIT_INSTALL_FAILED'),
165166
meta: {
166167
filesWritten: options.filesWritten,
167168
stderr: trimmed,
@@ -187,7 +188,7 @@ export function errorInitInvalidManifest(options: {
187188
return new CliStructuredError('CLI.INIT_INVALID_MANIFEST', `Failed to parse ${options.path}`, {
188189
why: `\`${options.path}\` is not valid JSON: ${options.cause}`,
189190
fix: `Fix the JSON syntax in \`${options.path}\` (a missing comma or unbalanced brace is the most common cause), then re-run \`prisma-next init\`.`,
190-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
191+
docsUrl: docsUrlFor('CLI.INIT_INVALID_MANIFEST'),
191192
meta: { path: options.path, cause: options.cause },
192193
});
193194
}
@@ -213,7 +214,7 @@ export function errorInitInvalidTsconfig(options: {
213214
return new CliStructuredError('CLI.INIT_INVALID_TSCONFIG', `Failed to parse ${options.path}`, {
214215
why: `\`${options.path}\` is not valid JSON or JSONC: ${options.cause}`,
215216
fix: `Fix the syntax in \`${options.path}\` and re-run \`prisma-next init\`. \`init\` accepts JSONC (comments and trailing commas) but cannot recover from unbalanced braces or missing commas.`,
216-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
217+
docsUrl: docsUrlFor('CLI.INIT_INVALID_TSCONFIG'),
217218
meta: { path: options.path, cause: options.cause },
218219
});
219220
}
@@ -239,7 +240,7 @@ export function errorInitProbeFailed(options: {
239240
return new CliStructuredError('CLI.INIT_PROBE_FAILED', 'Database probe failed', {
240241
why: `\`--probe-db\` could not complete and \`--strict-probe\` was set: ${options.cause}`,
241242
fix: 'Confirm `DATABASE_URL` points at a reachable server, or drop `--strict-probe` to treat probe failures as warnings.',
242-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
243+
docsUrl: docsUrlFor('CLI.INIT_PROBE_FAILED'),
243244
meta: {
244245
filesWritten: options.filesWritten,
245246
cause: options.cause,
@@ -261,7 +262,7 @@ export function errorInitEmitFailed(options: {
261262
return new CliStructuredError('CLI.INIT_EMIT_FAILED', 'Failed to emit contract', {
262263
why: `\`prisma-next contract emit\` failed: ${options.cause}`,
263264
fix: `Inspect your contract file, fix the underlying issue, then re-run \`${options.emitCommand}\`. Pass \`-v\` for the full error envelope.`,
264-
docsUrl: 'https://prisma-next.dev/docs/cli/contract-emit',
265+
docsUrl: docsUrlFor('CLI.INIT_EMIT_FAILED'),
265266
meta: {
266267
filesWritten: options.filesWritten,
267268
cause: options.cause,
@@ -294,7 +295,7 @@ export function errorInitSkillInstallFailed(options: {
294295
'Either:\n' +
295296
` - Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? ' --force' : ''}\` to skip the skill install for this run, or\n` +
296297
` - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
297-
docsUrl: 'https://prisma-next.dev/docs/cli/init#skills',
298+
docsUrl: docsUrlFor('CLI.INIT_SKILL_INSTALL_FAILED'),
298299
meta: {
299300
filesWritten: options.filesWritten,
300301
skillInstallCommand: options.skillInstallCommand,

packages/1-framework/3-tooling/cli/src/commands/init/init.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { execFile } from 'node:child_process';
22
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
33
import { promisify } from 'node:util';
44
import * as clack from '@clack/prompts';
5+
import { docsUrlFor } from '@prisma-next/utils/structured-error';
56
import { basename, dirname, isAbsolute, join } from 'pathe';
67
import { CliStructuredError } from '../../utils/cli-errors';
78
import { formatErrorJson, formatErrorOutput } from '../../utils/formatters/errors';
@@ -546,7 +547,7 @@ export async function runInit(
546547
{
547548
why: `The success document failed schema validation: ${String(validated)}`,
548549
fix: 'This is a bug in prisma-next. Please report it with the full `-v` output.',
549-
docsUrl: 'https://prisma-next.dev/docs/cli/init',
550+
docsUrl: docsUrlFor('CLI.INIT_INVALID_OUTPUT_DOCUMENT'),
550551
},
551552
),
552553
);

packages/1-framework/3-tooling/cli/test/cli-errors.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { docsUrlFor } from '@prisma-next/utils/structured-error';
12
import { describe, expect, it } from 'vitest';
23
import {
34
buildNeverPlannedFailure,
@@ -22,7 +23,7 @@ describe('CliStructuredError.toEnvelope()', () => {
2223
expect(envelope.fix).toBe(
2324
'Add a control-plane driver to prisma-next.config.ts (e.g. import a driver descriptor and set `driver: postgresDriver`)',
2425
);
25-
expect(envelope.docsUrl).toBe('https://prisma-next.dev/docs/cli/config');
26+
expect(envelope.docsUrl).toBe(docsUrlFor('CONFIG.DRIVER_REQUIRED'));
2627
});
2728

2829
it('converts readMarker error to envelope with CONFIG.FAMILY_READ_MARKER_REQUIRED', () => {
@@ -34,7 +35,7 @@ describe('CliStructuredError.toEnvelope()', () => {
3435
expect(envelope.fix).toBe(
3536
'Ensure family.verify.readMarker() is exported by your family package',
3637
);
37-
expect(envelope.docsUrl).toBe('https://prisma-next.dev/docs/cli/db-verify');
38+
expect(envelope.docsUrl).toBe(docsUrlFor('CONFIG.FAMILY_READ_MARKER_REQUIRED'));
3839
});
3940
});
4041

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { docsUrlFor } from '@prisma-next/utils/structured-error';
2+
import { describe, expect, it } from 'vitest';
3+
import { errorInitReinitNeedsForce } from '../../../src/commands/init/errors';
4+
5+
describe('init errors', () => {
6+
it('errorInitReinitNeedsForce links the canonical error-reference anchor for its code', () => {
7+
const error = errorInitReinitNeedsForce();
8+
expect(error.docsUrl).toBe(docsUrlFor('CLI.INIT_REINIT_NEEDS_FORCE'));
9+
});
10+
});

packages/1-framework/3-tooling/cli/test/errors.mapping.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { docsUrlFor } from '@prisma-next/utils/structured-error';
12
import { describe, expect, it } from 'vitest';
23
import { errorDriverRequired, errorFamilyReadMarkerSqlRequired } from '../src/utils/cli-errors';
34

@@ -10,7 +11,7 @@ describe('CliStructuredError.toEnvelope()', () => {
1011
code: 'CONFIG.DRIVER_REQUIRED',
1112
summary: 'Driver is required for DB-connected commands',
1213
fix: 'Add a control-plane driver to prisma-next.config.ts (e.g. import a driver descriptor and set `driver: postgresDriver`)',
13-
docsUrl: 'https://prisma-next.dev/docs/cli/config',
14+
docsUrl: docsUrlFor('CONFIG.DRIVER_REQUIRED'),
1415
});
1516
});
1617

@@ -22,7 +23,7 @@ describe('CliStructuredError.toEnvelope()', () => {
2223
code: 'CONFIG.FAMILY_READ_MARKER_REQUIRED',
2324
summary: 'Family readMarker() is required',
2425
fix: 'Ensure family.verify.readMarker() is exported by your family package',
25-
docsUrl: 'https://prisma-next.dev/docs/cli/db-verify',
26+
docsUrl: docsUrlFor('CONFIG.FAMILY_READ_MARKER_REQUIRED'),
2627
});
2728
});
2829
});

0 commit comments

Comments
 (0)