{"record":{"id":"414efd931e97feba","repo":"JuliusBrussee/caveman","slug":"native-runtime-decode-decision-w","errorCode":null,"errorMessage":"native runtime: decode decision: %w","messagePattern":"native runtime: decode decision: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/internal/nativeruntime/explain.go","lineNumber":55,"sourceCode":"\tobject, err := store.FindTaskDecision(decisionID)\n\tif err != nil {\n\t\treturn DecisionExplanation{}, err\n\t}\n\tvar record struct {\n\t\tSchema               string         `json:\"schema\"`\n\t\tDecisionID           string         `json:\"decision_id\"`\n\t\tTimestampMS          int64          `json:\"timestamp_ms\"`\n\t\tAction               string         `json:\"action\"`\n\t\tReason               string         `json:\"reason\"`\n\t\tInputBasis           map[string]any `json:\"input_basis\"`\n\t\tAlternativesRejected []string       `json:\"alternatives_rejected\"`\n\t\tTaskStateBefore      string         `json:\"task_state_before\"`\n\t\tTaskStateAfter       string         `json:\"task_state_after\"`\n\t\tCurrentness          string         `json:\"currentness\"`\n\t\tRecoveryRef          string         `json:\"recovery_ref\"`\n\t}\n\tif err := json.Unmarshal(object.Data, &record); err != nil {\n\t\treturn DecisionExplanation{}, fmt.Errorf(\"native runtime: decode decision: %w\", err)\n\t}\n\tif record.Schema != \"caveman.native.decision.v1\" || record.DecisionID != decisionID || record.Action == \"\" || record.Reason == \"\" {\n\t\treturn DecisionExplanation{}, errors.New(\"native runtime: invalid decision record\")\n\t}\n\treturn DecisionExplanation{\n\t\tSchema:               WhySchema,\n\t\tDecisionID:           record.DecisionID,\n\t\tSessionID:            object.SessionID,\n\t\tTimestampMS:          record.TimestampMS,\n\t\tAction:               record.Action,\n\t\tReason:               record.Reason,\n\t\tInputBasis:           record.InputBasis,\n\t\tAlternativesRejected: record.AlternativesRejected,\n\t\tTaskStateBefore:      record.TaskStateBefore,\n\t\tTaskStateAfter:       record.TaskStateAfter,\n\t\tCurrentness:          record.Currentness,\n\t\tRecoveryRef:          record.RecoveryRef,\n\t}, nil","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/internal/nativeruntime/explain.go#L37-L73","documentation":"nativeruntime.Explain decodes a stored decision object (object.Data) into a DecisionExplanation via json.Unmarshal. This error means the persisted bytes are not valid JSON — the record was truncated, corrupted, or written by an incompatible older schema. It wraps the encoding/json error so the exact offset/reason is preserved.","triggerScenarios":"Calling the explain/why API for a decisionID whose stored object.Data is truncated (crash mid-write), hand-edited, or produced by a newer/older binary with a different serialization; passing a decision ID whose blob collides with non-JSON data.","commonSituations":"Upgrading the runtime after a schema change without migrating the store; disk-full during an earlier record append; querying a decision whose row was partially written.","solutions":["Retry the explain call for a different, recent decisionID to see if only one record is corrupt","Inspect the stored object bytes for the decisionID to confirm truncation/corruption","If the store is disposable, remove or rotate the native runtime store so fresh records are written in the current schema","Check disk space and file permissions that could have caused the original short write"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"func looksLikeJSON(b []byte) bool {\n    return len(b) > 0 && b[0] == '{' && json.Valid(b)\n}","typeGuard":null,"tryCatchPattern":"exp, err := nativeruntime.Explain(ctx, decisionID)\nif err != nil {\n    if strings.Contains(err.Error(), \"decode decision\") {\n        // one corrupt record: log decisionID, surface 'explanation unavailable',\n        // keep serving other decisions — never retry the same corrupt blob\n        return explainUnavailable(decisionID)\n    }\n    return err\n}","preventionTips":["Write decision records atomically (temp file + rename) to avoid truncation","Version-stamp records (schema field) and migrate stores on upgrade","Monitor disk space on the store volume"],"tags":["json","decode","storage","corruption","nativeruntime"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}